{"_id":"@caspian-vega/transfer-state","_rev":"4-0881d13cf81a16f6021c98bb231348a4","name":"@caspian-vega/transfer-state","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@caspian-vega/transfer-state","version":"0.1.0","keywords":["astro","ssr","transfer-state","hydration","asynclocalstorage","islands"],"license":"MIT","_id":"@caspian-vega/transfer-state@0.1.0","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"4da471ce6e7889076674ea37d43bb1114d814085","tarball":"https://registry.npmjs.org/@caspian-vega/transfer-state/-/transfer-state-0.1.0.tgz","fileCount":29,"integrity":"sha512-ogvbBgQ+6e7K3F/VRD0WTYzRoiuli9cbdecXuldx+xYnzNpCCGTQHJLIttcIlTPJMsM1PaUFugHwevEQglv1Qw==","signatures":[{"sig":"MEQCIDsOAJWT1fMMEQIO/cpTCeKYD7ujHaZO7JcsAw90xtCKAiAMvCdGyggVgpthCBlYy7DPCy2FUu8GfGSzyxNyNRe4aQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":51059},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","browser":"./dist/browser-guard.js","require":"./dist/server.cjs"},"./package.json":"./package.json","./TransferStateClient.astro":"./components/TransferStateClient.astro"},"scripts":{"test":"vitest run","build":"vite build","typecheck":"tsc --noEmit"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"SSR→client transfer state for Astro. Collects values in an AsyncLocalStorage scope during rendering and replays them on the client, so work done on the server is not repeated in the browser.","directories":{},"sideEffects":["./dist/server.js","./dist/server.cjs","./dist/browser-guard.js"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.3.3","vitest":"^3.2.4","typescript":"^5.4.0","@types/node":"^25.9.1","vite-plugin-dts":"^5.0.1"},"peerDependencies":{"astro":">=4.0.0"},"peerDependenciesMeta":{"astro":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/transfer-state_0.1.0_1786861318469_0.9738883564345793","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@caspian-vega/transfer-state","version":"0.1.1","keywords":["astro","ssr","transfer-state","hydration","asynclocalstorage","islands"],"license":"MIT","_id":"@caspian-vega/transfer-state@0.1.1","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"b2b7a00427a38d036c4eb873da8fa3799b6c0ea5","tarball":"https://registry.npmjs.org/@caspian-vega/transfer-state/-/transfer-state-0.1.1.tgz","fileCount":29,"integrity":"sha512-WR1p6sM6Omcvgg75m61aSTWd8nzm1XL+T9KvUyC9myAHEB+1G4To693yfqMAmoFgvl1hHbAKZhsgCzBJFIvGjw==","signatures":[{"sig":"MEUCIQCK/S6fTPVQ6eziJJI+k2u0eb6WkdhlokMJmyDuUgXaVgIgOMQ5OrMZx/m70IN3V5j2ihOt359KICW1LY6zQntxGtA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50017},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","browser":"./dist/browser-guard.js","require":"./dist/server.cjs"},"./package.json":"./package.json","./TransferStateClient.astro":"./components/TransferStateClient.astro"},"scripts":{"test":"vitest run","build":"vite build","typecheck":"tsc --noEmit"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"SSR→client transfer state for Astro. Collects values in an AsyncLocalStorage scope during rendering and replays them on the client, so work done on the server is not repeated in the browser.","directories":{},"sideEffects":["./dist/server.js","./dist/server.cjs","./dist/browser-guard.js"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.3.3","vitest":"^3.2.4","typescript":"^5.4.0","@types/node":"^25.9.1","vite-plugin-dts":"^5.0.1"},"peerDependencies":{"astro":">=4.0.0"},"peerDependenciesMeta":{"astro":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/transfer-state_0.1.1_1786888895748_0.5974002570953671","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@caspian-vega/transfer-state","version":"0.2.0","keywords":["astro","ssr","transfer-state","hydration","asynclocalstorage","islands"],"license":"MIT","_id":"@caspian-vega/transfer-state@0.2.0","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"dist":{"shasum":"c7f4e0d95407dab44a1a292dfc1940ef4280f556","tarball":"https://registry.npmjs.org/@caspian-vega/transfer-state/-/transfer-state-0.2.0.tgz","fileCount":29,"integrity":"sha512-9m6BcHSQL+u24jivW5O5Lb1z+ZF+OdZjCL+XUlcyHgXQbFxh1DtVR8UMraxrDQEQz7DTZ7OKOySAMVdyG8cdeA==","signatures":[{"sig":"MEUCIGeAkKgSkiUNSgQq/f9AURlu2+pmS395biERR4ZhKeTPAiEAjzXSBvTa04gFTIPIkp/+IBgrw7Wxd3qC12TFFNQSspA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54811},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","browser":"./dist/browser-guard.js","require":"./dist/server.cjs"},"./package.json":"./package.json","./TransferStateClient.astro":"./components/TransferStateClient.astro"},"scripts":{"test":"vitest run","build":"vite build","typecheck":"tsc --noEmit"},"_npmUser":{"name":"caspianvega","email":"dom.konstantin@tutanota.com"},"description":"SSR→client transfer state for Astro. Collects values in an AsyncLocalStorage scope during rendering and replays them on the client, so work done on the server is not repeated in the browser.","directories":{},"sideEffects":["./dist/server.js","./dist/server.cjs","./dist/browser-guard.js"],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.2.1","vitest":"^4.1.10","typescript":"^5.4.0","@types/node":"^25.9.1","vite-plugin-dts":"^5.0.3"},"peerDependencies":{"astro":">=4.0.0"},"peerDependenciesMeta":{"astro":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/transfer-state_0.2.0_1786941831958_0.2971617182662345","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@caspian-vega/transfer-state","version":"0.2.1","description":"SSR→client transfer state for Astro. Collects values in an AsyncLocalStorage scope during rendering and replays them on the client, so work done on the server is not repeated in the browser.","type":"module","license":"MIT","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","browser":"./dist/browser-guard.js","import":"./dist/server.js","require":"./dist/server.cjs"},"./TransferStateClient.astro":"./components/TransferStateClient.astro","./package.json":"./package.json"},"sideEffects":["./dist/server.js","./dist/server.cjs","./dist/browser-guard.js"],"peerDependencies":{"astro":">=4.0.0"},"peerDependenciesMeta":{"astro":{"optional":true}},"devDependencies":{"@types/node":"^25.9.1","typescript":"^5.4.0","vite":"^8.2.1","vite-plugin-dts":"^5.0.3","vitest":"^4.1.10"},"keywords":["astro","ssr","transfer-state","hydration","asynclocalstorage","islands"],"publishConfig":{"access":"public"},"scripts":{"build":"vite build","test":"vitest run","typecheck":"tsc --noEmit"},"_nodeVersion":"24.15.0","_id":"@caspian-vega/transfer-state@0.2.1","dist":{"integrity":"sha512-4TELD8S/HqfXT1+1lOf6wwZZbUPZDAMjeID+mOYdgmIwRbxuTwF7hCfAK4GlzRse8weHWf7sXFYYtixtvZsebg==","shasum":"7f03dc47c53cc09e1e244a633fbbfd859ac22fc8","tarball":"https://registry.npmjs.org/@caspian-vega/transfer-state/-/transfer-state-0.2.1.tgz","fileCount":29,"unpackedSize":53883,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBHf1PwDf8TcoS2A6zQbLsNFSr168GNZd3zAeRMlalLsAiEA7hWk7JKCE7KII3XFcGOEoCWuWIPrjBFNI0yTnB7HSl4="}]},"_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/transfer-state_0.2.1_1787213017337_0.3565925155956784"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-16T06:21:58.254Z","modified":"2026-08-20T08:03:37.702Z","0.1.0":"2026-08-16T06:21:58.608Z","0.1.1":"2026-08-16T14:01:35.890Z","0.2.0":"2026-08-17T04:43:52.159Z","0.2.1":"2026-08-20T08:03:37.496Z"},"license":"MIT","keywords":["astro","ssr","transfer-state","hydration","asynclocalstorage","islands"],"description":"SSR→client transfer state for Astro. Collects values in an AsyncLocalStorage scope during rendering and replays them on the client, so work done on the server is not repeated in the browser.","maintainers":[{"name":"caspianvega","email":"dom.konstantin@tutanota.com"}],"readme":"# @caspian-vega/transfer-state\n\nSSR to client transfer state for Astro. Values produced while rendering are collected per request,\nemitted with the document, and read back on the client instead of being recomputed.\n\nPart of [caspian-vega-astro-libs](https://codeberg.org/caspian-vega/caspian-vega-astro-libs).\n\n## Install\n\n```bash\nnpm install @caspian-vega/transfer-state\n# or\npnpm add @caspian-vega/transfer-state\n```\n\nAstro is an optional peer dependency, needed only for the `.astro` component.\n\n## Setup\n\nTwo server-side steps.\n\n1. Open a scope per request in `src/middleware.ts`:\n\n```ts\nimport { sequence } from 'astro/middleware';\n\nimport { transferStateMiddleware } from '@caspian-vega/transfer-state/server';\n\nexport const onRequest = sequence(transferStateMiddleware, ...yourOtherMiddleware);\n```\n\n2. Emit the payload as the last element of the layout, after `</body>` and inside `</html>`:\n\n```astro\n---\nimport TransferStateClient from '@caspian-vega/transfer-state/TransferStateClient.astro';\n---\n\n<html>\n    <head></head>\n    <body>\n        <slot />\n    </body><TransferStateClient />\n</html>\n```\n\nThe component drains the bucket when it renders. Anything collected by a component rendered after it\ndoes not reach the client.\n\n## Usage\n\n```ts\nimport { transferAction } from '@caspian-vega/transfer-state';\n\nexport const loadPost = transferAction('post:byId', (id: string) => api.getPost(id));\n\n// Server: calls api.getPost and stores the result.\n// Client: returns the stored result, api.getPost is not called.\nconst post = await loadPost('42');\n```\n\nManual store and read:\n\n```ts\nimport { addTransferState, getTransferStateValue, makeStateKey } from '@caspian-vega/transfer-state';\n\nconst key = makeStateKey({ resource: 'posts', page: 1 });\n\naddTransferState(key, posts);\nconst restored = getTransferStateValue(key);\n```\n\n## Resolution order\n\n```mermaid\nflowchart TD\n  Call[transferAction call] --> Scope{Server request scope open?}\n  Scope -->|yes| Run[Run callback]\n  Run --> Store[Write bucket under id + args hash]\n  Store --> Ret1[Return value]\n  Scope -->|no| Snap{Key present in snapshot?}\n  Snap -->|yes| Read[Read and consume]\n  Read --> Ret2[Return stored value]\n  Snap -->|no| Run2[Run callback]\n  Run2 --> Ret3[Return value]\n```\n\n## Entry points\n\n`node:async_hooks` cannot be bundled for the client, so the package is split.\n\n| Import                                                   | Contains                                              | Client safe          |\n| -------------------------------------------------------- | ----------------------------------------------------- | -------------------- |\n| `@caspian-vega/transfer-state`                           | `transferAction`, read and write helpers, key hashing | yes                  |\n| `@caspian-vega/transfer-state/server`                    | scope, middleware, drain                              | no                   |\n| `@caspian-vega/transfer-state/TransferStateClient.astro` | payload component                                     | server rendered only |\n\nImport `/server` in `middleware.ts` and `.astro` frontmatter only. Importing it in a client build\nfails at import time with a message naming the fix.\n\n## API\n\n### transferAction(id, callback)\n\nWraps a callback so its server result is reused on the client.\n\n- `id` (string, required): stable name, combined with the call arguments to form the key.\n- `callback` (`(...args) => TResult`, required): sync or async. Its result must be JSON-serializable.\n- Returns: `TransferAction<TArgs, TResult>`, an async function with the callback's parameters.\n\n```ts\nconst loadFeed = transferAction('feed:list', (topic: string) => api.getFeed(topic));\nconst feed = await loadFeed('astro');\n```\n\n### makeStateKey(key)\n\nBuilds a stable short key.\n\n- `key` (`string | object`, required): JSON-serializable description. Property order is part of the\n  result, so build it identically on both sides.\n- Returns: `string`\n\n### getTransferState()\n\n- Returns: `TransferStateBucket | undefined`. The collecting bucket on the server, the snapshot on\n  the client, `undefined` when neither exists.\n\n### getTransferStateValue(key, options?)\n\nReads one value.\n\n- `key` (string, required): key returned by `makeStateKey`.\n- `options.consumeOnce` (boolean, default `true`): remove the entry after reading.\n- Returns: `unknown`, or `undefined` when the key is absent.\n\n```ts\nconst value = getTransferStateValue(key, { consumeOnce: false });\n```\n\n### addTransferState(key, value)\n\nStores a value for the current request. No-op on the client, so callers do not branch on the\nenvironment.\n\n- `key` (string, required)\n- `value` (unknown, required): must be JSON-serializable.\n- Returns: `void`\n\n### isCollecting()\n\n- Returns: `boolean`, `true` inside a server request scope.\n\n### createTransferState()\n\n- Returns: `TransferStateAccess`, an object carrying `isCollecting`, `makeStateKey`,\n  `getTransferState`, `getTransferStateValue`, `addTransferState` and `transferAction`. Register it\n  as a value in any container.\n\n```ts\nexport class AccessTransferState {\n    private readonly access = createTransferState();\n}\n```\n\n### TRANSFER_STATE_GLOBAL\n\n`string`, name of the `globalThis` property holding the client snapshot.\n\n### transferStateMiddleware\n\nAstro middleware opening one scope per request. Register it before any middleware that renders.\n\n- Signature: `(context, next) => Promise<Response>`\n\n### runWithTransferState(fn, bucket?)\n\nRuns `fn` inside a fresh scope. Everything awaited within it reaches the same bucket.\n\n- `fn` (`() => T`, required)\n- `bucket` (`TransferStateBucket`, default `{}`): pre-seeded bucket.\n- Returns: `T`\n\n```ts\nconst html = runWithTransferState(() => render(page));\n```\n\n### getRequestTransferState()\n\n- Returns: `TransferStateBucket | undefined`, the bucket of the in-flight request.\n\n### isTransferStateActive()\n\n- Returns: `boolean`, `true` inside a scope opened by `runWithTransferState`.\n\n### drainTransferState()\n\nReads the collected state and clears the bucket, so a second render pass cannot emit it twice.\n\n- Returns: `TransferStateBucket`\n\n## Types\n\n### TransferAction\n\n`(...args: TArgs) => Promise<Awaited<TResult>>`\n\n### TransferStateBucket\n\n`Record<string, unknown>` holding one request's collected values.\n\n### GetOptions\n\n- `consumeOnce` (boolean, optional): remove the entry after reading. Default `true`.\n\n### TransferStateAccess\n\n`ReturnType<typeof createTransferState>`\n\n## Constraints\n\n- Values travel through a `<script>` tag, so they must be JSON-serializable. No `Date`, `Map`,\n  `undefined` inside objects, or class instances.\n- Reads consume by default. Pass `{ consumeOnce: false }` for a value read more than once.\n- The payload is plain text in the HTML. It is per request, not shared between users, but fully\n  visible to the recipient.\n- View transitions are supported. The snapshot is read on every access, so a `<ClientRouter />` swap\n  is picked up.\n- Requires a Node-compatible SSR runtime providing `node:async_hooks`.\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":""}