{"_id":"@1kbirds/palimpsest-client","name":"@1kbirds/palimpsest-client","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@1kbirds/palimpsest-client","version":"0.1.0","description":"Type-safe TypeScript wrapper around palimpsest-client-js, with React hooks.","license":"MIT OR Apache-2.0","repository":{"type":"git","url":"git+https://github.com/ThousandBirdsInc/reactive-palimpsest.git","directory":"packages/palimpsest-client-typescript"},"publishConfig":{"access":"public"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js"}},"scripts":{"build":"tsc -p tsconfig.build.json","typecheck":"tsc -p tsconfig.json --noEmit","clean":"rm -rf dist"},"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"devDependencies":{"@types/react":"^18.3.3","react":"^18.3.1","typescript":"^5.5.3"},"gitHead":"04ad85196514110e8b8099d8c55efda040012e12","_id":"@1kbirds/palimpsest-client@0.1.0","bugs":{"url":"https://github.com/ThousandBirdsInc/reactive-palimpsest/issues"},"homepage":"https://github.com/ThousandBirdsInc/reactive-palimpsest#readme","_nodeVersion":"26.3.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-a6LcH3ou2w28XDtns4iUq+iQEeFzTLzN/w8anUew0gakm0Fs9IU8PSac0w2jXepCeUzq0FIsM1O65A/OUAzJNg==","shasum":"243e307ef3d4a4ca296eee2c7a64dd6e2181a2db","tarball":"https://registry.npmjs.org/@1kbirds/palimpsest-client/-/palimpsest-client-0.1.0.tgz","fileCount":42,"unpackedSize":97241,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHX7yaZDW7qBcQEJvwyd3Db7jz5ayK27aOpI6Gc9pdCuAiEApxuc/bq5sW5OHEufHSdB0pXMNiPYw2voirqyMgm5xyk="}]},"_npmUser":{"name":"thousandbirds","email":"colton@thousandbirds.ai"},"directories":{},"maintainers":[{"name":"thousandbirds","email":"colton@thousandbirds.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/palimpsest-client_0.1.0_1782186132362_0.6127693809520283"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-23T03:42:12.226Z","0.1.0":"2026-06-23T03:42:12.496Z","modified":"2026-06-23T03:42:12.669Z"},"maintainers":[{"name":"thousandbirds","email":"colton@thousandbirds.ai"}],"description":"Type-safe TypeScript wrapper around palimpsest-client-js, with React hooks.","homepage":"https://github.com/ThousandBirdsInc/reactive-palimpsest#readme","repository":{"type":"git","url":"git+https://github.com/ThousandBirdsInc/reactive-palimpsest.git","directory":"packages/palimpsest-client-typescript"},"bugs":{"url":"https://github.com/ThousandBirdsInc/reactive-palimpsest/issues"},"license":"MIT OR Apache-2.0","readme":"# `@1kbirds/palimpsest-client`\n\nType-safe TypeScript wrapper around\n[`palimpsest-client-js`](../../crates/palimpsest-client-js) (the\n`wasm-pack --target web` bundle), with React hooks.\n\nThe wasm bundle handles transport, codec, and reconnection. This\npackage adds:\n\n- Strongly-typed subscriptions: declare a row type once,\n  `client.subscribe<Post>(sql)` projects each diff into typed\n  objects.\n- Schema-aware row decoder with optional per-column overrides\n  (custom JSON parsing, `bigint → number` coercion, etc.).\n- React hooks (`usePalimpsestClient`, `usePalimpsestSubscription`)\n  that handle wasm init, connection caching, and tear-down — no\n  manual lifecycle in your components.\n\nThe package has **zero runtime coupling** to a specific wasm bundle\nlocation. You import the wasm-pack output yourself and pass the\nmodule to `PalimpsestClient.connect`, which keeps the package\nbundler-agnostic (Vite, webpack, esbuild, Rollup all work).\n\n## Install\n\nIn a monorepo with the wasm crate built into a sibling directory:\n\n```json\n{\n  \"dependencies\": {\n    \"@1kbirds/palimpsest-client\": \"^0.1.0\",\n    \"react\": \"^18.3.1\"\n  }\n}\n```\n\nThe wasm bundle (built via `wasm-pack build crates/palimpsest-client-js --target web`)\nis imported directly from your app.\n\n## Quick start (vanilla TS)\n\n```ts\nimport { PalimpsestClient } from \"@1kbirds/palimpsest-client\";\nimport * as wasm from \"./pkg/palimpsest_client_js\";\n\ninterface Post {\n  id: bigint;\n  title: string;\n  published: boolean;\n}\n\nconst client = await PalimpsestClient.connect({\n  url: \"http://localhost:50051\",\n  wasm,\n});\n\nconst sub = await client.subscribe<Post>(\n  \"SELECT id, title, published FROM posts\",\n);\n\nsub.onEvent((event) => {\n  switch (event.kind) {\n    case \"accepted\":\n      console.log(\"schema:\", event.schema);\n      break;\n    case \"diff\":\n      console.log(event.op, event.rows);  // Post[]\n      break;\n    case \"resync\":\n      console.warn(\"resync:\", event.reason);\n      break;\n    case \"error\":\n      console.error(event.code, event.message);\n      break;\n  }\n});\n\n// later\nawait sub.unsubscribe();\nawait client.shutdown();\n```\n\n## Quick start (React)\n\n```tsx\nimport { useMemo } from \"react\";\nimport {\n  usePalimpsestClient,\n  usePalimpsestSubscription,\n} from \"@1kbirds/palimpsest-client/react\";\nimport * as wasm from \"./pkg/palimpsest_client_js\";\n\ninterface Post {\n  id: bigint;\n  title: string;\n  published: boolean;\n}\n\nexport function App() {\n  const opts = useMemo(\n    () => ({ url: \"http://localhost:50051\", wasm }),\n    [],\n  );\n  const { client } = usePalimpsestClient(opts);\n\n  const { status, rows, error } = usePalimpsestSubscription<Post>(\n    client,\n    \"SELECT id, title, published FROM posts\",\n  );\n\n  if (status !== \"open\") return <p>status: {status}</p>;\n  if (error) return <pre>{error.message}</pre>;\n  return (\n    <ul>\n      {rows.map((p) => (\n        <li key={String(p.id)}>\n          #{String(p.id)} {p.title}\n        </li>\n      ))}\n    </ul>\n  );\n}\n```\n\n## Row decoders\n\nThe wasm side widens Postgres `bigint`/`numeric` columns to JS\n`bigint` and `string` respectively to avoid silent precision loss.\nIf your app prefers plain `number` for small integers, pass a\ncolumn-level decoder:\n\n```ts\nconst sub = await client.subscribe<Post>(sql, {\n  decoder: {\n    coerceSafeIntegersToNumber: true,\n    decoders: {\n      created_at: (v) => new Date(String(v)),\n    },\n  },\n});\n```\n\nDecoders attached on the client (`PalimpsestClient.connect({ decoder\n})`) apply to every subscription; per-subscription decoders override\nthem.\n\n## What's typed today vs. in flight\n\nThe wire protocol delivers a snapshot (`accepted` + initial rows)\nsynchronously; live diffs over the same subscription will arrive\nonce the server's WAL-streaming path is wired through\n`SubscriptionRouter::pump_cursor` (see `DESIGN.md` §18.5). Until\nthen, set `refreshKey` on the hook to force a fresh subscribe after\na write — the hook supports this without any other code changes.\n\n## Subpaths\n\n- `@1kbirds/palimpsest-client` — core (`PalimpsestClient`, `TypedSubscription`,\n  types). No React dependency.\n- `@1kbirds/palimpsest-client/react` — hooks. Pulls `react` as a peer.\n","readmeFilename":"README.md","_rev":"1-5e00f714542e547264bfbb14ff7674fc"}