{"_id":"@caspian-vega/small-vial","_rev":"7-d5bd278172662369cc425b840452ee81","name":"@caspian-vega/small-vial","dist-tags":{"latest":"0.5.0","rc":"0.5.0-rc.2"},"versions":{"0.1.0":{"name":"@caspian-vega/small-vial","version":"0.1.0","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","_id":"@caspian-vega/small-vial@0.1.0","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"abb8a199bbc122915d6543eb10371f6d91c037ae","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.1.0.tgz","fileCount":8,"integrity":"sha512-tEs0zvjy/q/uOITwbw+Lpcyys4O0RmSdZcag+l0cDbVWXm3vmYSLRrgyyJwG2ZiXK8Gz3nmjRU5+/LYlyB0MZA==","signatures":[{"sig":"MEQCIGVxH1JyQzbPkMcyavT3ISzgkjRVPktV455Lm3KXaqObAiBRInXHm/hHw2y3SaxfjHzmwy96JARzXwlMXGCqOiax9Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":120929},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"}},"private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","build:vite":"vite build"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^7.3.1","vitest":"^3.2.4","typescript":"^5.8.3","@types/node":"^22.15.27"},"peerDependencies":{"vite":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/small-vial_0.1.0_1786805353300_0.485827242029772","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@caspian-vega/small-vial","version":"0.1.1","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","_id":"@caspian-vega/small-vial@0.1.1","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"9994cd6874039c03395e4fd68dda641090280b6d","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.1.1.tgz","fileCount":8,"integrity":"sha512-OfAj23eA6G6WzJwy4kZtrv45UDP+bv4gVIJGwzC6pKvWqodfIAPa2jOZswXXgxyTme0q399oxzCnkx8RJlbQ2A==","signatures":[{"sig":"MEUCIQCVIVIhZybT94Iwavyj7ES1+bPwJaTM6kqEx9KHZmeFBQIgWZ6Hqn2eYc4igKBIJQX3tkp0ftDK+VaWC9DYrYLbs9s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":114144},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"}},"private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","build:vite":"vite build"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^7.3.1","vitest":"^3.2.4","typescript":"^5.8.3","@types/node":"^22.15.27"},"peerDependencies":{"vite":"^6.0.0 || ^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/small-vial_0.1.1_1786888861607_0.06412304307699124","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@caspian-vega/small-vial","version":"0.2.0","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","_id":"@caspian-vega/small-vial@0.2.0","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"0b48189144af88303cc92f1f568039db6e7f50d7","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.2.0.tgz","fileCount":8,"integrity":"sha512-YwVXrYfQ5xquL/r5gqDeczOvzvQPoR25diIl51oPJSgpLr/06TzR5NzQsFuvnhxjAdje2nRZIkngPhBOaE9Dow==","signatures":[{"sig":"MEQCIAc1Ai7YImfZRV7xt/TP6/fRC4g3afpdR+fOYPVRx6XlAiAEdZ0lN+IOmj4RYg+G0k6wQepQLEc3ibk5GjaZd06eew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":114263},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"}},"private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","build:vite":"vite build"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.2.1","vitest":"^4.1.10","typescript":"^5.8.3","@babel/core":"^8.0.1","@types/node":"^25.9.1","@rolldown/plugin-babel":"^0.2.3","@babel/plugin-proposal-decorators":"^8.0.2"},"peerDependencies":{"vite":"^7.0.0 || ^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/small-vial_0.2.0_1786941793529_0.25360050612612706","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@caspian-vega/small-vial","version":"0.2.1","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","_id":"@caspian-vega/small-vial@0.2.1","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"1fe9c87f90676a0cadd8d98743181cbccfe12b8f","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.2.1.tgz","fileCount":8,"integrity":"sha512-Pl0rNnTLKZeJTC7dIlrroDglmuPGnu3plqKkiuegR0/MHy+g1yR0HZ1OztUTEydjAC7Bfd2ggby/jisOwbxw+w==","signatures":[{"sig":"MEYCIQCc+5YMWhzPqGty9vFXv06mwlSTOBxOZ7zZDbK6YUx+CgIhAOz0+ckfACFXmCPsA/m8vHHXm0IXSntTYquMdY9xUP1D","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":112621},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"}},"private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","build:vite":"vite build"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.2.1","vitest":"^4.1.10","typescript":"^5.8.3","@babel/core":"^8.0.1","@types/node":"^25.9.1","@rolldown/plugin-babel":"^0.2.3","@babel/plugin-proposal-decorators":"^8.0.2"},"peerDependencies":{"vite":"^7.0.0 || ^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/small-vial_0.2.1_1787212991555_0.32579152903662356","host":"s3://npm-registry-packages-npm-production"}},"0.5.0-rc.1":{"name":"@caspian-vega/small-vial","version":"0.5.0-rc.1","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","_id":"@caspian-vega/small-vial@0.5.0-rc.1","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"a623d0fbb1290c55dc7629af70a6add57431dc35","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.5.0-rc.1.tgz","fileCount":24,"integrity":"sha512-w+oGe9yC9gUf5HL3B4lBA5Q2scJFt+y3EVF6fULLs504L6JAYyrRvhhGTt1ydTi5qEwz9y9xpXqOZ6Bjkztocw==","signatures":[{"sig":"MEUCIQCvs13OZtIaj6COwSwa0C6vQTo7V7kCMq7kSjq/lg3vqQIgMptG5D/YIcu/anmfe/hLjOPb8pSWfrTyVuitk3VwKpM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":193240},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vite":{"types":"./dist/vite.d.ts","import":"./dist/vite.js","browser":"./dist/vite-browser-guard.js"},"./astro":{"types":"./dist/astro.d.ts","import":"./dist/astro.js","browser":"./dist/astro-browser.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./package.json":"./package.json","./astro-middleware":{"types":"./dist/astro-middleware.d.ts","import":"./dist/astro-middleware.js"}},"private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","directories":{},"sideEffects":["./dist/server.js","./dist/astro-middleware.js","./dist/vite-browser-guard.js"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.2.1","astro":"^7.1.6","vitest":"^4.1.10","typescript":"^5.8.3","@babel/core":"^8.0.1","@types/node":"^25.9.1","@rolldown/plugin-babel":"^0.2.3","@babel/plugin-proposal-decorators":"^8.0.2"},"peerDependencies":{"vite":"^7.0.0 || ^8.0.0","astro":">=4.0.0","@babel/core":"^7.0.0 || ^8.0.0","@rolldown/plugin-babel":">=0.2.0","@babel/plugin-proposal-decorators":"^7.0.0 || ^8.0.0"},"peerDependenciesMeta":{"vite":{"optional":true},"astro":{"optional":true},"@babel/core":{"optional":true},"@rolldown/plugin-babel":{"optional":true},"@babel/plugin-proposal-decorators":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/small-vial_0.5.0-rc.1_1787299354365_0.1970037387238992","host":"s3://npm-registry-packages-npm-production"}},"0.5.0-rc.2":{"name":"@caspian-vega/small-vial","version":"0.5.0-rc.2","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","_id":"@caspian-vega/small-vial@0.5.0-rc.2","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"532bc7c7b75710a1b77c8d6a1a2f22ac120b1587","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.5.0-rc.2.tgz","fileCount":24,"integrity":"sha512-vxIg0i8shKRbDQ/FMHtx+krEyS4HzQxWbjdbvtGNyLw2Hl5+wOSJuICrMbp5+bunIOOdcOdiQ7M69j5mCP2XKg==","signatures":[{"sig":"MEUCIQDxtzfOISt1KAh9xVCaXyx2HkfVg2+iOCFr1zlG4WF93QIgA5Mw1RCfK+jMoaO15Gegen+uADwE65puw3Vhk4P7mns=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":192939},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vite":{"types":"./dist/vite.d.ts","import":"./dist/vite.js","browser":"./dist/vite-browser-guard.js"},"./astro":{"types":"./dist/astro.d.ts","import":"./dist/astro.js","browser":"./dist/astro-browser.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./package.json":"./package.json","./astro-middleware":{"types":"./dist/astro-middleware.d.ts","import":"./dist/astro-middleware.js"}},"private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","directories":{},"sideEffects":["./dist/server.js","./dist/astro-middleware.js","./dist/vite-browser-guard.js"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.2.1","astro":"^7.1.6","vitest":"^4.1.10","typescript":"^5.8.3","@babel/core":"^8.0.1","@types/node":"^25.9.1","@rolldown/plugin-babel":"^0.2.3","@babel/plugin-proposal-decorators":"^8.0.2"},"peerDependencies":{"vite":"^7.0.0 || ^8.0.0","astro":">=4.0.0","@babel/core":"^7.0.0 || ^8.0.0","@rolldown/plugin-babel":">=0.2.0","@babel/plugin-proposal-decorators":"^7.0.0 || ^8.0.0"},"peerDependenciesMeta":{"vite":{"optional":true},"astro":{"optional":true},"@babel/core":{"optional":true},"@rolldown/plugin-babel":{"optional":true},"@babel/plugin-proposal-decorators":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/small-vial_0.5.0-rc.2_1787303931834_0.4148327865670911","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@caspian-vega/small-vial","private":false,"version":"0.5.0","description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js"},"./astro":{"types":"./dist/astro.d.ts","browser":"./dist/astro-browser.js","import":"./dist/astro.js"},"./astro-middleware":{"types":"./dist/astro-middleware.d.ts","import":"./dist/astro-middleware.js"},"./vite":{"types":"./dist/vite.d.ts","browser":"./dist/vite-browser-guard.js","import":"./dist/vite.js"},"./package.json":"./package.json"},"keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"author":"","license":"MIT","devDependencies":{"@babel/core":"^8.0.1","@babel/plugin-proposal-decorators":"^8.0.2","@rolldown/plugin-babel":"^0.2.3","@types/node":"^25.9.1","tsup":"^8.5.0","typescript":"^5.8.3","vite":"^8.2.1","vitest":"^4.1.10","astro":"^7.1.6"},"peerDependencies":{"@babel/core":"^7.0.0 || ^8.0.0","@babel/plugin-proposal-decorators":"^7.0.0 || ^8.0.0","@rolldown/plugin-babel":">=0.2.0","astro":">=4.0.0","vite":"^7.0.0 || ^8.0.0"},"publishConfig":{"access":"public"},"sideEffects":["./dist/server.js","./dist/astro-middleware.js","./dist/vite-browser-guard.js"],"peerDependenciesMeta":{"@babel/core":{"optional":true},"@babel/plugin-proposal-decorators":{"optional":true},"@rolldown/plugin-babel":{"optional":true},"astro":{"optional":true},"vite":{"optional":true}},"scripts":{"dev":"tsup --watch","build":"tsup","test":"vitest run","typecheck":"tsc --noEmit"},"_nodeVersion":"24.15.0","_id":"@caspian-vega/small-vial@0.5.0","dist":{"integrity":"sha512-aIAj/2UaqgwndJb1MVc/9wjpcvjzIgFqJ5P1dGJvXEG2QlZyjJfL+mp37DID0yGmmRBOSrO1HfsWQQJ5C/HXZg==","shasum":"66fd22d65d29604da76612934d0ef7aae6fc496d","tarball":"https://registry.npmjs.org/@caspian-vega/small-vial/-/small-vial-0.5.0.tgz","fileCount":24,"unpackedSize":231171,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDm7bGHt6PNUMNu8ANS1nfiuaN4Vk7abn6ZL+ZN8RhnYAiBS4TJs3K6ymHj3ZuvwGm+ZwQUSaY4BjyKQJXa7XQtcgQ=="}]},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"directories":{},"maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/small-vial_0.5.0_1787411833073_0.754374720006826"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-15T14:49:13.162Z","modified":"2026-08-22T15:17:13.451Z","0.1.0":"2026-08-15T14:49:13.456Z","0.1.1":"2026-08-16T14:01:01.764Z","0.2.0":"2026-08-17T04:43:13.678Z","0.2.1":"2026-08-20T08:03:11.700Z","0.5.0-rc.1":"2026-08-21T08:02:34.516Z","0.5.0-rc.2":"2026-08-21T09:18:52.163Z","0.5.0":"2026-08-22T15:17:13.294Z"},"license":"MIT","keywords":["dependency-injection","di","typescript","astro","ssr","svelte"],"description":"A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"readme":"# @caspian-vega/small-vial\n\nDependency injection for Astro and other Vite-based SSR apps. Decorator registration, an\n`svInject()` call usable anywhere, and request-scoped containers.\n\n**Vite is required.** SSR detection and the dev/test/production branches read `import.meta.env`,\nwhich Vite replaces at build time. Bundlers that do not define it (webpack, Turbopack, esbuild on\nits own, bare Node ESM) are out of scope: the first `svInject(...)` throws there. Astro, Vite SSR\nand Vitest are the supported hosts.\n\nPart of [caspian-vega-astro-libs](https://codeberg.org/caspian-vega/caspian-vega-astro-libs).\n\n## Table of contents\n\n- [Install](#install)\n- [Usage](#usage)\n- [Setup](#setup)\n    - [Decorators](#decorators)\n    - [Three ways to set it up](#three-ways-to-set-it-up)\n    - [Keeping decorated class names](#keeping-decorated-class-names)\n    - [Bootstrap](#bootstrap)\n    - [SSR detection](#ssr-detection)\n- [Request scoping](#request-scoping)\n- [Testing](#testing)\n- [Astro entry: tokens, middleware, integration](#astro-entry-tokens-middleware-integration)\n- [Entry points](#entry-points)\n- [API](#api)\n    - [Decorators: Service, Store, Injectable, Controller, Util](#servicebehaviour-store-injectable-controller-util)\n    - [Injection: svInject, svInjectOptional, svSecureInject](#svinjecttoken)\n    - [Registration: provide, svEject, initContainer, teardownTestContainer](#provideprovision)\n    - [Configuration: createToken, setGlobalAppConfig, appConfig, setOptOutDefaultContainer](#createtokenid)\n    - [Server entry: makeInjectionContext and context strategies](#makeinjectioncontextcallback-config-contextstrategy)\n    - [Discovery: discoverAppConfig](#discoverappconfigsrcdir)\n    - [Bootstrap env: readBootstrapEnv](#readbootstrapenv)\n    - [SvDebugLogger](#svdebuglogger)\n- [Lifecycle hooks](#lifecycle-hooks)\n- [Types](#types)\n- [Constraints](#constraints)\n- [Synergies](#synergies)\n- [License](#license)\n\n## Install\n\n```bash\nnpm install @caspian-vega/small-vial\n# or\npnpm add @caspian-vega/small-vial\n```\n\n## Usage\n\n```ts\nimport { type PostConstructable, Service, svInject } from '@caspian-vega/small-vial';\n\n@Service()\nclass UserService implements PostConstructable {\n    postConstruct() {}\n\n    getUser(id: string) {\n        return this.users.get(id);\n    }\n}\n\n@Service()\nclass AuthService {\n    private userService = svInject(UserService);\n\n    authenticate(credentials: Credentials) {\n        return this.userService.getUser(credentials.userId);\n    }\n}\n\nconst auth = svInject(AuthService);\n```\n\nConstructor injection uses the same call:\n\n```ts\n@Service()\nclass AuthService {\n    constructor(private userService = svInject(UserService)) {}\n}\n```\n\n## Setup\n\n### Decorators\n\nThe library uses canonical TC39 stage-3 decorators (`2023-11`), not the legacy TypeScript pipeline.\nAll five decorators are class decorators, so the toolchain must transform them before the runtime\nsees them.\n\n- Astro 6: transforms through esbuild, no extra setup.\n- Astro 7 and Vite 8: build with OXC, which regressed decorator support. OXC does not lower\n  `2023-11` class decorators yet, tracked in\n  [oxc-project/oxc#9170](https://github.com/oxc-project/oxc/issues/9170). Decorated files have to\n  run through `@babel/plugin-proposal-decorators` first, or every `@Service()` is dropped and\n  nothing registers. The Babel step is a workaround for that regression, not a permanent\n  requirement.\n\nThe transform ships with the package, so the Babel wiring is one integration entry. Treat it as\ntemporary: once decorator support lands in OXC and ships through Vite into Astro, the Babel pass and\nits optional peers can be dropped and `smallVial({ decorators: false })` is enough.\n\nInstall the optional peers it drives:\n\n```bash\nnpm install --save-dev @babel/core @babel/plugin-proposal-decorators @rolldown/plugin-babel\n# or\npnpm add -D @babel/core @babel/plugin-proposal-decorators @rolldown/plugin-babel\n```\n\n```ts\n// astro.config.ts\nimport { defineConfig } from 'astro/config';\n\nimport { smallVial } from '@caspian-vega/small-vial/astro';\n\nexport default defineConfig({\n    integrations: [smallVial()],\n});\n```\n\n`smallVial()` installs the decorator plugin and sets `vite.ssr.target` to `'node'`.\n\nOutside Astro, use the vite plugin directly:\n\n```ts\n// vite.config.ts\nimport { smallVialDecorators } from '@caspian-vega/small-vial/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n    plugins: [smallVialDecorators()],\n    ssr: { target: 'node' },\n});\n```\n\nLegacy decorator versions are not supported. The plugin always applies `version: '2023-11'`.\n\n### Three ways to set it up\n\n```mermaid\nflowchart TD\n  Q{What does the app need?}\n  Q -->|SSR tokens on every route| A[\"smallVial with middleware true<br/>built-in middleware, sane defaults\"]\n  Q -->|Own providers, session, or middleware order| B[\"smallVial plus createDiMiddleware<br/>in src/middleware.ts\"]\n  Q -->|Routes without DI, or a non-Astro server| C[\"Hand rolled<br/>own tokens plus makeInjectionContext\"]\n```\n\n**1. Integration with the built-in middleware.** The default, and what most apps want.\n\n```ts\n// astro.config.ts\nintegrations: [smallVial({ middleware: true })];\n```\n\nEvery request gets its own container and the 7 [SSR tokens](#request-scoping). No middleware module\nneeded, and the integration bootstraps the middleware for you: it serializes the bootstrap flags into\n`import.meta.env.SMALL_VIAL_BOOTSTRAP`, and the injected entry applies them before the first request.\nPass a function when they depend on the command:\n\n```ts\nintegrations: [smallVial({ middleware: true, bootstrap: ({ isDev }) => ({ debug: isDev }) })];\n```\n\nGlobal providers are found by convention on the server side. The integration looks for\n`src/app.small-vial.config.ts`, then `src/config/app.small-vial.config.ts`, and hands the export to\n`createDiMiddleware({ appConfig })`:\n\n```ts\n// src/app.small-vial.config.ts\nimport type { ApplicationConfig } from '@caspian-vega/small-vial';\n\nexport const appConfig: ApplicationConfig = [{ token: API_URL, provide: 'https://api.example.com' }];\n```\n\n`default`, `appConfig` and `globalAppConfig` are all accepted as the export name. Point the\nintegration somewhere else with `smallVial({ appConfig: 'src/core/configs/app.config.ts' })`, or turn\ndiscovery off with `appConfig: false`.\n\nDiscovery belongs to the built-in middleware and only runs when `middleware` is enabled. With your\nown middleware module the option is inert, whatever it is set to, and the global providers are yours\nto pass, see setup 2 below.\n\nThe client stays manual and its location is yours to pick, ideally the first script in the head of\nthe root layout, see [Bootstrap](#bootstrap).\n\n**2. Integration with your own middleware.** Same defaults, but the middleware is yours, so it can\ncarry global and request-scoped providers, sessions, and a position in `sequence(...)`.\n\n```ts\n// src/middleware.ts\nimport { sequence } from 'astro/middleware';\n\nimport { bootstrapDi, createDiMiddleware } from '@caspian-vega/small-vial/astro';\n\nimport { globalAppConfig } from './core/configs/app.config.ts';\n\nbootstrapDi({ appConfig: globalAppConfig });\n\nexport const onRequest = sequence(otherMiddleware, createDiMiddleware({ session: true }));\n```\n\nKeep `middleware` off in `smallVial(...)`, otherwise both run.\n\nWith `middleware` off there is no app config discovery either, so `smallVial({ appConfig })` is not\nread and does not need `appConfig: false`. `bootstrapDi(...)` in the middleware module is what seeds\nthe global providers, and it takes the live `ApplicationConfig` rather than a path, so tokens stay\ntokens instead of going through the serialized bootstrap payload.\n\n**3. Hand rolled.** Own tokens, own SSR config, own call to `makeInjectionContext(...)`.\n\n```ts\n// src/middleware.ts\nimport { defineMiddleware } from 'astro/middleware';\n\nimport { type ApplicationConfig, createToken } from '@caspian-vega/small-vial';\nimport { makeInjectionContext } from '@caspian-vega/small-vial/server';\n\nexport const REQUEST = createToken<Request>('REQUEST');\n\nexport const onRequest = defineMiddleware(async (context, next) => {\n    if (context.url.pathname.startsWith('/health')) return next();\n\n    const ssrConfig: ApplicationConfig = [{ token: REQUEST, provide: context.request }];\n\n    return makeInjectionContext(async () => next(), ssrConfig);\n});\n```\n\nRecommendation: take 1, move to 2 as soon as there are global or request-scoped providers. Reach for\n3 only for corner cases: excluding routes from DI, a token set that is not the Astro `APIContext`,\nor a server that is not Astro.\n\nDetails, ordering rules and the session strategy are in\n[MIDDLEWARE_SETUP.md](https://codeberg.org/caspian-vega/caspian-vega-astro-libs/src/branch/master/libs/small-vial/MIDDLEWARE_SETUP.md).\n\n### Keeping decorated class names\n\n`smallVialDecorators()` also keeps the binding name of a decorated class. The `2023-11` transform\notherwise drops it, so `@Service() class UserService {}` comes out anonymous. Injection is unaffected\neither way, since the container keys on the class reference, but stack traces, breakpoints and\n`SvDebugLogger` output lose the name. Turn it off with `smallVialDecorators({ keepClassNames: false })`.\n\n### Bootstrap\n\n`setGlobalAppConfig(config)` must run before the first injection. It seeds the global providers, and\na container created before it exists resolves without them.\n\nBoth sides need their own call. The middleware only covers SSR.\n\n```mermaid\nflowchart TD\n  Q{Where does the code run?}\n  Q -->|Server render| S[\"Discovered app.small-vial.config.ts,<br/>or module scope of the DI middleware\"]\n  Q -->|Browser| C[\"head script of the root layout<br/>runs before any island hydrates\"]\n  S --> I[First svInject resolves with global providers]\n  C --> I\n```\n\nServer, discovered by the integration. Export the providers from\n`src/app.small-vial.config.ts` and nothing else is needed:\n\n```ts\n// src/app.small-vial.config.ts\nexport const appConfig: ApplicationConfig = [{ token: API_URL, provide: 'https://api.example.com' }];\n```\n\nServer, written by hand, at module scope in `src/middleware/diContext.middleware.ts`. It is\ndeliberately outside the middleware handler, so it runs at import time rather than per request.\n`bootstrapDi(...)` bundles the three calls:\n\n```ts\nimport { bootstrapDi, createDiMiddleware } from '@caspian-vega/small-vial/astro';\n\nimport { globalAppConfig } from '../core/configs/app.config.ts';\n\nbootstrapDi({ appConfig: globalAppConfig, optOutDefaultContainer: true });\n\nexport const DiMiddleware = createDiMiddleware();\n```\n\nBrowser, in the `<head>` of the root layout, before the islands below it hydrate:\n\n```astro\n<head>\n    <script>\n        import { setGlobalAppConfig } from '@caspian-vega/small-vial';\n        import { globalAppConfig } from '../core/configs/app.config.ts';\n\n        setGlobalAppConfig(globalAppConfig);\n    </script>\n</head>\n```\n\nRules that hold on both sides:\n\n- Call it once, from one bootstrap location per side.\n- Call it before any `svInject(...)`, including the ones in `.astro` frontmatter and in island\n  module scope.\n- Keep the config itself in a shared module, so server and client seed the same providers.\n- On SSR the global config is not request-scoped. Request-bound providers go into the second\n  argument of `makeInjectionContext(...)`, and code that may run on the client must treat them as\n  optional.\n\nSee `playground/src/middleware/diContext.middleware.ts` and `playground/src/layouts/Layout.astro`.\n\n### SSR detection\n\nDetection reads `import.meta.env.SSR`, which Vite inlines per build: `true` in the server bundle,\n`false` in the client bundle. There is no runtime override, and none is needed on a supported host.\nVitest applies the same replacement, so tests branch correctly too.\n\n## Request scoping\n\n`makeInjectionContext()` gives each request its own container, so concurrent renders never share\ninstances and request-bound values can be injected anywhere.\n\n```mermaid\nsequenceDiagram\n  participant M as Middleware\n  participant C as makeInjectionContext\n  participant K as Container\n  participant R as Render\n  M->>C: callback, ssrConfig\n  C->>K: provideContainer(config)\n  K-->>C: container\n  C->>R: run callback\n  R->>K: svInject(token)\n  K-->>R: instance\n  R-->>C: Response\n  C->>K: hibernate + cleanContainer\n  C-->>M: Response\n```\n\nAstro middleware, with the tokens and the request wiring from the `/astro` entry:\n\n```ts\nimport { bootstrapDi, createDiMiddleware } from '@caspian-vega/small-vial/astro';\n\nimport { globalAppConfig } from '../core/configs/app.config.ts';\n\nbootstrapDi({ appConfig: globalAppConfig });\n\nexport const DiMiddleware = createDiMiddleware({\n    // extra request-scoped providers, registered after the SSR tokens\n    providers: ({ context }) => [{ token: REQUEST_ID, provide: context.request.headers.get('x-request-id') }],\n});\n```\n\nThe 7 SSR tokens are provided on every request:\n\n| Token                | Value                |\n| -------------------- | -------------------- |\n| `SSR_COOKIE`         | `context.cookies`    |\n| `SSR_REQUEST`        | `context.request`    |\n| `SSR_LOCALS`         | `context.locals`     |\n| `SSR_REQUEST_PARAMS` | `context.params`     |\n| `SSR_URL`            | `context.url`        |\n| `SSR_SESSION`        | `context.session`    |\n| `SSR_ACTION_CONTEXT` | `context.callAction` |\n\nCode that also runs on the client must read them with `svSecureInject(...)`, since they only exist\nduring a server render.\n\nFor a session-scoped container, let the middleware resolve the session id and hand it to the\nstrategy:\n\n```ts\nexport const DiMiddleware = createDiMiddleware({\n    session: true,\n    contextStrategy: ({ sessionId }) =>\n        sessionId ? SessionBasedContextStrategy(sessionId, { ttl: 900_000, strategy: 'stale-access' }) : undefined,\n});\n```\n\nWriting the middleware by hand stays supported, and is what `createDiMiddleware` does internally:\n\n```ts\nexport const DiMiddleware: MiddlewareHandler = defineMiddleware(async (context, next) => {\n    const ssrConfig: ApplicationConfig = [{ token: SSR_COOKIE, provide: context.cookies }];\n\n    return makeInjectionContext(async () => next(), ssrConfig);\n});\n```\n\nWhatever renders the app must be called inside the callback, otherwise injection has no context to\nresolve from.\n\n## Testing\n\nVitest resolves through the same container as the app. Two things have to be set up per test file.\n\n- The test runner must lower the decorators. Vitest reads `vite.config.ts`, so `smallVialDecorators()`\n  from [Decorators](#decorators) belongs in that file's `plugins`, even when the app itself is wired\n  through the `smallVial()` integration.\n- `setOptOutDefaultContainer(true)` must run before the first injection. Without it every\n  `svInject(...)` outside a request context resolves from a fresh container, so instances are never\n  shared and stubs never arrive.\n\nWith the opt out in place, injections in test mode share one container per file. Reset it between\ntests with `teardownTestContainer()`.\n\n```ts\nimport {\n    Service,\n    createToken,\n    provide,\n    setGlobalAppConfig,\n    setOptOutDefaultContainer,\n    svInject,\n    teardownTestContainer,\n} from '@caspian-vega/small-vial';\nimport { afterEach, beforeEach, describe, expect, it } from 'vitest';\n\nconst API_URL = createToken<string>('API_URL');\n\n@Service()\nclass UserService {\n    getUser(id: string) {\n        return { id, name: 'real' };\n    }\n}\n\n@Service()\nclass AuthService {\n    private users = svInject(UserService);\n    private apiUrl = svInject(API_URL);\n\n    whoAmI(id: string) {\n        return `${this.users.getUser(id).name}@${this.apiUrl}`;\n    }\n}\n\ndescribe('AuthService', () => {\n    beforeEach(() => {\n        setOptOutDefaultContainer(true);\n        setGlobalAppConfig([{ token: API_URL, provide: 'http://test' }]);\n    });\n\n    afterEach(() => teardownTestContainer());\n\n    it('resolves real collaborators', () => {\n        expect(svInject(AuthService).whoAmI('1')).toBe('real@http://test');\n    });\n\n    it('uses a stub when one is provided first', () => {\n        provide({ token: UserService, provide: { getUser: () => ({ id: '1', name: 'stub' }) } });\n\n        expect(svInject(AuthService).whoAmI('1')).toBe('stub@http://test');\n    });\n});\n```\n\nStubbing rules that follow from the container:\n\n- Register the stub before the code under test resolves the token. The first registration wins, and\n  an eager class is created on its first resolve.\n- `provide({ token, provide })` stubs a singleton, `provide({ token, factory })` stubs a value that\n  is rebuilt on every injection.\n- `setGlobalAppConfig(...)` seeds providers for containers created afterwards, so call it in\n  `beforeEach`, not at module scope after an injection already ran.\n\nRequest-scoped providers are tested by running the assertion inside the context:\n\n```ts\nimport { makeInjectionContext } from '@caspian-vega/small-vial/server';\n\nconst REQ = createToken<{ url: string }>('REQ');\n\nit('resolves request providers', async () => {\n    const url = await makeInjectionContext(async () => svInject(REQ).url, [{ token: REQ, provide: { url: '/posts' } }]);\n\n    expect(url).toBe('/posts');\n});\n```\n\nSee `libs/small-vial/tests` for the library's own specs.\n\n## Astro entry: tokens, middleware, integration\n\n### createDiMiddleware(options?)\n\nAstro middleware that opens a request-scoped container and provides the Astro `APIContext` under the\nSSR tokens.\n\n- `options.providers` (`ApplicationConfig | (ctx) => ApplicationConfig | Promise<ApplicationConfig>`,\n  optional): extra request-scoped providers, registered after the SSR tokens.\n- `options.contextStrategy` (`ContainerContextStrategy | (ctx) => ContainerContextStrategy | undefined`,\n  optional): defaults to `defaultContainerContextStrategy()`.\n- `options.session` (`boolean | { key?: string; generateId?: () => string }`, default `false`): read or\n  create a session-bound id before the container opens, defaults `key` to `'sv-session'` and\n  `generateId` to `crypto.randomUUID`. The id is passed to the `contextStrategy` factory.\n- `options.bootstrap` (`boolean | DiBootstrapEnvOptions`, default `false`): call `bootstrapDi(...)`\n  while the middleware is created. Merge order is `defaultBootstrapOptions`, the env payload written\n  by `smallVial(...)`, then this object.\n- `options.appConfig` (`ApplicationConfig | (() => ApplicationConfig)`, optional): global providers,\n  seeded with `setGlobalAppConfig(...)` while the middleware is created. The integration fills this\n  from the discovered config module.\n- Returns: `MiddlewareHandler`\n\n```ts\nexport const onRequest = createDiMiddleware({ session: true });\n```\n\n### bootstrapDi(options?)\n\nSeeds the global configuration. Runs once per side, at module scope, before the first `svInject(...)`.\n\n- `options.appConfig` (`ApplicationConfig`, optional): global providers.\n- `options.optOutDefaultContainer` (boolean, default `true`): disable the client-only default container.\n- `options.debug` (boolean, default `false`): enable `SvDebugLogger`.\n- Returns: `void`\n\n### smallVial(options?)\n\nAstro integration.\n\n- `options.decorators` (`boolean | SmallVialDecoratorsOptions`, default `true`): install the decorator\n  plugin. The object form is forwarded to `smallVialDecorators(...)`.\n- `options.ssrTargetNode` (boolean, default `true`): set `vite.ssr.target` to `'node'`.\n- `options.middleware` (`boolean | { order?: 'pre' | 'post' }`, default `false`): add the built-in DI\n  middleware. Leave it off when the order relative to other middleware matters, and export\n  `createDiMiddleware(...)` from a middleware module instead.\n- `options.appConfig` (`string | false`, default: discovery): global providers for the server side.\n  Only consulted when `middleware` is enabled, since it feeds the built-in middleware. With\n  `middleware: false` nothing is discovered and the option is inert, seed the providers with\n  `bootstrapDi(...)` in your own middleware module instead. Discovery order is `<srcDir>/app.small-vial.config.ts`, then\n  `<srcDir>/config/app.small-vial.config.ts`, with `.ts`, `.mts`, `.js` and `.mjs` accepted. A string\n  is a path relative to the project root, or an absolute one. The module exports the\n  `ApplicationConfig` as `default`, `appConfig` or `globalAppConfig`.\n- `options.bootstrap` (`boolean | DiBootstrapEnvOptions | (ctx) => DiBootstrapEnvOptions | false`,\n  default: `true` when `middleware` is enabled, `false` otherwise): bootstrap flags for the injected\n  middleware. `ctx` is `{ command, isDev, config }`. The resolved object is serialized into\n  `import.meta.env.SMALL_VIAL_BOOTSTRAP` and `process.env.SMALL_VIAL_BOOTSTRAP`.\n- Returns: `AstroIntegration`\n\n```ts\nsmallVial({ middleware: true, bootstrap: ({ command }) => ({ debug: command === 'dev' }) });\n```\n\n### discoverAppConfig(srcDir)\n\nLooks up the app config module below the Astro source directory.\n\n- `srcDir` (`URL`, required): Astro `config.srcDir`.\n- Returns: `string | undefined`, absolute path of the first match.\n\n`resolveAppConfigPath(appConfig, root)` is the manual counterpart, it resolves a path against the\nproject root and throws when the file is missing. `APP_CONFIG_BASENAME`, `APP_CONFIG_LOCATIONS` and\n`APP_CONFIG_EXTENSIONS` hold the convention.\n\n### readBootstrapEnv()\n\nReads the payload the integration wrote, from `import.meta.env` and then `process.env`.\n\n- Returns: `DiBootstrapEnvOptions | undefined`\n\n`SMALL_VIAL_BOOTSTRAP_ENV_KEY` is the key it reads, `defaultBootstrapOptions` is\n`{ optOutDefaultContainer: true, debug: false }`.\n\n### smallVialDecorators(options?)\n\nVite plugin that lowers `2023-11` decorators through Babel.\n\n- `options.keepClassNames` (boolean, default `true`): keep the binding name of a decorated class.\n- `options.filter` (object, default `{ code: '@' }`): pre-filter deciding which modules reach Babel.\n- `options.babelPlugins` (`PluginItem[]`, optional): extra Babel plugins, appended after the decorators\n  plugin.\n- Returns: `Promise<Plugin>`, resolved by vite from the plugins array.\n\nExported from `@caspian-vega/small-vial/vite`, together with `keepDecoratedClassNames` for a\nhand-built Babel preset.\n\n## Entry points\n\n| Import                                      | Contains                                                                     |\n| ------------------------------------------- | ---------------------------------------------------------------------------- |\n| `@caspian-vega/small-vial`                  | Decorators, injection, registration, configuration                           |\n| `@caspian-vega/small-vial/server`           | Everything above plus `makeInjectionContext` and the context strategies      |\n| `@caspian-vega/small-vial/astro`            | SSR tokens, `bootstrapDi`, `createDiMiddleware`, the `smallVial` integration |\n| `@caspian-vega/small-vial/astro-middleware` | `onRequest`, the ready-made middleware for `addMiddleware(...)`              |\n| `@caspian-vega/small-vial/vite`             | `smallVialDecorators`, the build-time decorator plugin                       |\n\n`/astro` needs `astro`, `/vite` needs `@babel/core`, `@babel/plugin-proposal-decorators` and\n`@rolldown/plugin-babel`. All four are optional peer dependencies, installed only by the entries that\nuse them.\n\n## API\n\n### Service(behaviour?), Store, Injectable, Controller, Util\n\nClass decorators that register the decorated class. They behave identically and differ only in the\narchetype they name.\n\n- `behaviour` (`'EAGER' | 'LAZY'`, default `'EAGER'`): `'LAZY'` registers the class without\n  instantiating it until `provide(...)` is called.\n- Returns: a class decorator.\n\n```ts\n@Service()\nclass Mailer {}\n\n@Store('LAZY')\nclass DraftStore {}\n```\n\n### InjectableTSEX(behaviour?)\n\nSame as `Injectable`, for the legacy TypeScript experimental decorator pipeline.\n\n- `behaviour` (`'EAGER' | 'LAZY'`, default `'EAGER'`)\n- Returns: a class decorator.\n\n### svInject(token)\n\nResolves from the current injection context, global or request-scoped.\n\n- `token` (`Tokenizable<T> | Class<T>`, required)\n- Returns: `T`\n- Throws: `Error` when nothing is registered for the token.\n\n```ts\nconst auth = svInject(AuthService);\n```\n\n### svInjectOptional(token)\n\n- `token` (`Tokenizable<T>`, required)\n- Returns: `T | undefined`\n\n```ts\nconst value = svInjectOptional(SSR_ONLY_TOKEN);\n```\n\n### svSecureInject(token)\n\nReturns a resolver for a token that may be absent, so one implementation can read from SSR on the\nserver and fall back to the browser on the client.\n\n- `token` (`Tokenizable<T>`, required)\n- Returns: `SafeInjectFn<T>`. Call it with a default value, or call `secureHandle(fn)` to receive\n  the resolved value or `undefined` and branch yourself.\n\n```ts\n@Store()\nexport class AppMetaStore {\n    private SSRCookie = svSecureInject<AstroCookies>(SSR_COOKIE);\n\n    public readonly isDark = atom(\n        this.SSRCookie.secureHandle((value) =>\n            value ? value.get('theme')?.value === 'dark' : this.loadThemeFromCSRCookie() === 'dark'\n        )\n    );\n}\n```\n\n### provide(provision)\n\nRegisters a provider, factory, or lazy class in the current container.\n\n- `provision` (`Tokenizable<T> | Provider | Class<T>`, required)\n- Returns: `void`\n\n```ts\nprovide({ token: LOGGER, factory: () => new ConsoleLogger() });\nprovide(LazyService);\n```\n\n### svEject(token, force?)\n\nRemoves an instance from the container and runs its `onEject()`.\n\n- `token` (`Tokenizable | Class<T>`, required)\n- `force` (boolean, default `false`): remove even when other holders remain.\n- Returns: `boolean`, `true` when something was removed.\n\n```ts\nonDestroy(() => svEject(LazyService));\n```\n\n### createToken(id)\n\nCreates a typed token.\n\n- `id` (string, required): unique, non-empty. Duplicate ids collide silently.\n- Returns: `Tokenizable<T>`\n\n```ts\nconst API_URL = createToken<string>('API_URL');\n```\n\n### initContainer()\n\nReturns the current container, creating it when needed.\n\n- Returns: `Container`\n\n### teardownTestContainer()\n\nDrops the test container.\n\n- Returns: `void`\n\n```ts\nafterEach(() => teardownTestContainer());\n```\n\n### setGlobalAppConfig(config)\n\nSets global and client-side providers. Not request-scoped on SSR.\n\n- `config` (`ApplicationConfig`, required)\n- Returns: `void`\n- Must run before any container is initialized, that is before the first `svInject(...)`. Call it\n  once per side: at module scope in the DI middleware on the server, and in a `<head>` script of the\n  root layout in the browser. See [Bootstrap](#bootstrap).\n\n```ts\nsetGlobalAppConfig([{ token: API_URL, provide: 'https://api.example.com' }]);\n```\n\n### appConfig()\n\n- Returns: `ApplicationConfig`, the currently registered global config.\n\n### setOptOutDefaultContainer(value)\n\nDisables the client-only default container. Required for SSR request scoping.\n\n- `value` (boolean, required)\n- Returns: `void`\n\n### getSSRStorage()\n\n- Returns: the `AsyncLocalStorage` instance holding request containers, or `undefined` before the\n  server entry is imported.\n\n### CONTAINER_KEY\n\n`string`, key under which the request container is stored.\n\n### makeInjectionContext(callback, config?, contextStrategy?)\n\nServer entry only. Runs `callback` inside a request-scoped container.\n\n- `callback` (`() => Promise<T>`, required): must contain whatever renders the app.\n- `config` (`ApplicationConfig`, optional): request-scoped providers.\n- `contextStrategy` (`ContainerContextStrategy`, optional): defaults to\n  `defaultContainerContextStrategy()`.\n- Returns: `Promise<T>`, the callback's result.\n\nTeardown runs whether the callback resolves or throws.\n\n```ts\nreturn makeInjectionContext(async () => next(), ssrConfig);\n```\n\n### defaultContainerContextStrategy()\n\n- Returns: `ContainerContextStrategy` that creates a fresh container per request, calls\n  `postConstruct()`, and clears it with `CLEAN_ALL()` afterwards.\n\n### SessionBasedContextStrategy(sessionId, sessionPolicy)\n\nKeeps containers in memory and reuses them per session id.\n\n- `sessionId` (string, required): use the Astro session id in Astro setups.\n- `sessionPolicy.ttl` (number, required): lifetime in milliseconds.\n- `sessionPolicy.strategy` (`'stale-access' | 'since-created'`, required): `'stale-access'`\n  refreshes the TTL on each access, `'since-created'` does not.\n- Returns: `ContainerContextStrategy`\n\n```ts\nreturn makeInjectionContext(\n    async () => next(),\n    ssrConfig,\n    SessionBasedContextStrategy(sessionId, {\n        ttl: 900_000,\n        strategy: 'stale-access',\n    })\n);\n```\n\nConstraints of this strategy:\n\n- The whole container graph stays in memory per active session.\n- Lifecycle hooks become load-bearing, since `onHibernate` and `onResume` run between requests.\n- Sockets, polling, intervals and timers must clean up explicitly or they leak.\n- Session containers are never serialized.\n- Behind a proxy or load balancer, session affinity on the session id cookie must be enabled.\n\n### SvDebugLogger\n\nNamespace export.\n\n```ts\nimport { SvDebugLogger } from '@caspian-vega/small-vial';\n\nSvDebugLogger.default.enable();\n```\n\n## Lifecycle hooks\n\n| Interface           | Method            | Runs when                                                            |\n| ------------------- | ----------------- | -------------------------------------------------------------------- |\n| `PostConstructable` | `postConstruct()` | The container is ready and the instance has been created             |\n| `Ejectable`         | `onEject()`       | `svEject(...)` removes the instance, or the container is cleared     |\n| `Hibernateable`     | `onHibernate()`   | A request completes and the container is kept. Session strategy only |\n| `Resumable`         | `onResume()`      | A kept container is reused for a new request. Session strategy only  |\n\n```ts\n@Service()\nclass LiveFeedService implements PostConstructable, Ejectable, Hibernateable, Resumable {\n    postConstruct() {}\n    onHibernate() {}\n    onResume() {}\n    onEject() {}\n}\n```\n\n## Types\n\n### Class\n\n`(new (...args: any[]) => T)` carrying optional `_service_prop`, `_service_tag`, `_service_lazy`.\n\n### Tokenizable\n\n- `prototype` (`Class<T>`, optional)\n- `id` (string, optional)\n\n### Provider\n\n- `token` (`Tokenizable<T> | Class<T>`, required)\n- `provide` (`T`, optional): instance or value, a singleton within the container.\n- `factory` (`() => T`, optional): runs on every injection.\n\n```ts\nconst config: ApplicationConfig = [\n    { token: LOGGER, factory: () => (isDevMode() ? new ConsoleLogger() : new RemoteLogger()) },\n];\n```\n\n### ApplicationConfig\n\n`Provider[]`\n\n### DiBootstrapEnvOptions\n\n`BootstrapDiOptions` without `appConfig`, that is the serializable half.\n\n- `optOutDefaultContainer` (boolean, optional)\n- `debug` (boolean, optional)\n\n### SafeInjectFn\n\n- Call signature `(defaultValue: T) => T`\n- `secureHandle<S>(handler: (value: T | undefined) => S): S`\n\n### SafeInjector\n\n- `get(): T`\n- `getOrElse(defaultValue: T): T`\n\n### ContainerContextStrategy\n\n- `provideContainer(config?: ApplicationConfig): Container`\n- `cleanContainer?(container: Container): void`\n\n### Container\n\n- `postConstruct(): void`\n- `registerProvider(token, instance): void`\n- `registerFactory(token, factory): void`\n- `loadConfig(config, overwrite?): void`\n- `eject(token, force?): boolean`\n- `CLEAN_ALL(): void`\n- `hibernate(): void`\n- `resume(): void`\n\n## Constraints\n\n- Registered classes are singletons within their container. Use a `factory` provider for\n  non-singleton instances.\n- Factories run on every injection. Use `provide` with a pre-built instance for singleton behaviour,\n  or memoize inside the factory and manage that cache.\n- Registration is eager by default. `'LAZY'` registers without instantiating, and the instance\n  appears only after an explicit `provide(...)`.\n- Circular dependencies are not resolved. Move the injection into `postConstruct`, into the method\n  that needs it, or use `svSecureInject`.\n- Injecting at provider-creation time does not work. Use `postConstruct` or inject on demand.\n- There is no island or module scope. Add containers to the root injection context yourself, or use\n  factories for narrower scopes.\n- Requires SSR mode, at least standalone, and a promise-based render cycle.\n\n## Synergies\n\nSee [SYNERGIES.md](https://codeberg.org/caspian-vega/caspian-vega-astro-libs/src/branch/master/SYNERGIES.md).\n\n## License\n\nMIT\n","readmeFilename":""}