@vielzeug/vault 3.0.0 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/adapter-core.cjs +1 -1
  2. package/dist/adapter-core.cjs.map +1 -1
  3. package/dist/adapter-core.d.ts +5 -34
  4. package/dist/adapter-core.d.ts.map +1 -1
  5. package/dist/adapter-core.js +1 -282
  6. package/dist/adapter-core.js.map +1 -1
  7. package/dist/adapters/indexeddb.cjs +1 -1
  8. package/dist/adapters/indexeddb.cjs.map +1 -1
  9. package/dist/adapters/indexeddb.d.ts +5 -35
  10. package/dist/adapters/indexeddb.d.ts.map +1 -1
  11. package/dist/adapters/indexeddb.js +1 -365
  12. package/dist/adapters/indexeddb.js.map +1 -1
  13. package/dist/adapters/memory.cjs +1 -1
  14. package/dist/adapters/memory.cjs.map +1 -1
  15. package/dist/adapters/memory.d.ts +8 -3
  16. package/dist/adapters/memory.d.ts.map +1 -1
  17. package/dist/adapters/memory.js +1 -60
  18. package/dist/adapters/memory.js.map +1 -1
  19. package/dist/adapters/sqlite.cjs +14 -32
  20. package/dist/adapters/sqlite.cjs.map +1 -1
  21. package/dist/adapters/sqlite.d.ts.map +1 -1
  22. package/dist/adapters/sqlite.js +14 -409
  23. package/dist/adapters/sqlite.js.map +1 -1
  24. package/dist/adapters/webstorage.cjs +1 -1
  25. package/dist/adapters/webstorage.cjs.map +1 -1
  26. package/dist/adapters/webstorage.d.ts +0 -6
  27. package/dist/adapters/webstorage.d.ts.map +1 -1
  28. package/dist/adapters/webstorage.js +1 -160
  29. package/dist/adapters/webstorage.js.map +1 -1
  30. package/dist/codec.cjs +1 -1
  31. package/dist/codec.cjs.map +1 -1
  32. package/dist/codec.js +1 -5
  33. package/dist/codec.js.map +1 -1
  34. package/dist/errors.cjs +1 -1
  35. package/dist/errors.cjs.map +1 -1
  36. package/dist/errors.d.ts +0 -5
  37. package/dist/errors.d.ts.map +1 -1
  38. package/dist/errors.js +1 -21
  39. package/dist/errors.js.map +1 -1
  40. package/dist/index.cjs +1 -1
  41. package/dist/index.d.ts +1 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -5
  44. package/dist/indexeddb.cjs +1 -1
  45. package/dist/indexeddb.d.ts +2 -2
  46. package/dist/indexeddb.d.ts.map +1 -1
  47. package/dist/indexeddb.js +1 -2
  48. package/dist/internal.cjs +1 -1
  49. package/dist/internal.cjs.map +1 -1
  50. package/dist/internal.js +1 -72
  51. package/dist/internal.js.map +1 -1
  52. package/dist/local-storage.js +1 -2
  53. package/dist/memory.js +1 -2
  54. package/dist/session-storage.js +1 -2
  55. package/dist/sqlite.js +1 -2
  56. package/dist/ttl.cjs +1 -1
  57. package/dist/ttl.cjs.map +1 -1
  58. package/dist/ttl.js +1 -24
  59. package/dist/ttl.js.map +1 -1
  60. package/dist/types.cjs +1 -1
  61. package/dist/types.cjs.map +1 -1
  62. package/dist/types.d.ts +0 -2
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js +1 -27
  65. package/dist/types.js.map +1 -1
  66. package/dist/vault.cjs +1 -1
  67. package/dist/vault.cjs.map +1 -1
  68. package/dist/vault.iife.js +1 -1
  69. package/dist/vault.iife.js.map +1 -1
  70. package/dist/vault.js +1 -1
  71. package/dist/vault.js.map +1 -1
  72. package/package.json +6 -6
  73. package/dist/query.cjs +0 -2
  74. package/dist/query.cjs.map +0 -1
  75. package/dist/query.d.ts +0 -23
  76. package/dist/query.d.ts.map +0 -1
  77. package/dist/query.js +0 -54
  78. package/dist/query.js.map +0 -1
@@ -1,2 +1,2 @@
1
- const e=require("./codec.cjs"),t=require("./errors.cjs"),n=require("./internal.cjs"),r=require("./query.cjs"),i=require("./ttl.cjs");function a(e){if(e.length===0)throw new t.VaultError(`batch: declare at least one table`)}function o(e,t,n){return n!==void 0&&i.assertPositiveFinite(n,`put/putAll`),n??e[t].defaultTtl}function s(e,r,i,a,o){let s=n.getRecordKey(e,r,a);if(s!==i)throw new t.VaultError(`${o}: key field "${e[r].key}" must be "${String(i)}" but got "${String(s)}" in table "${r}"`)}function c(n){return(r,i)=>{let a=n?.[r];if(!a)return i;try{return e.validateRecord(a,i)}catch(e){throw new t.VaultError(`validation failed for table "${r}"`,{cause:e})}}}function l(e,i,a,c,l,u=()=>!0){let d=c??((e,t)=>t),f=e=>{if(!u())throw new t.VaultScopeError(`transaction context is no longer active`);if(l&&!l.has(e))throw new t.VaultScopeError(`table "${e}" is not part of this batch scope`)},p={async clear(e){f(e),await i.clear(e),a(e)},async count(e){return f(e),i.count?i.count(e):(await i.getAll(e)).length},async delete(e,t){f(e);let n=await i.delete(e,t);return n&&a(e),n},async deleteMany(e,t){if(f(e),!i.deleteMany){let n=(await Promise.all(t.map(t=>i.delete(e,t)))).filter(Boolean).length;return n>0&&a(e),n}let n=await i.deleteMany(e,t);return n>0&&a(e),n},async get(e,t){return f(e),i.get(e,t)},async getAll(e){return f(e),i.getAll(e)},async getMany(e,t){return f(e),i.getMany?i.getMany(e,t):Promise.all(t.map(t=>i.get(e,t)))},async has(e,t){return f(e),i.has?i.has(e,t):await i.get(e,t)!==void 0},async isEmpty(e){return await p.count(e)===0},iterate(e){return f(e),i.iterate?i.iterate(e):(async function*(){let t=await i.getAll(e);for(let e of t)yield e})()},async keys(t,n){if(f(t),!n&&i.getAllKeys)return i.getAllKeys(t);let r=await i.getAll(t),a=n?r.filter(n):r,o=e[t].key;return a.map(e=>e[o])},async put(t,n,r){f(t);let s=d(t,n);return await i.put(t,s,o(e,t,r)),a(t),s},async putAll(t,n,r){if(f(t),n.length===0)return;let s=n.map(e=>d(t,e));await i.putAll(t,s,o(e,t,r)),a(t)},query(t){return f(t),r.createQueryBuilder({deleteMany:async r=>{let i=r.map(r=>n.getRecordKey(e,t,r));return p.deleteMany(t,i)},source:()=>p.getAll(t)})},async update(t,n,r,i){let a=await p.get(t,n);if(a===void 0)return;let o={...a,...r};return s(e,t,n,o,`update`),p.put(t,o,i)},async upsert(t,n,r,i){let a=r(await p.get(t,n));return s(e,t,n,a,`upsert: update()`),p.put(t,a,i)}};return p}function u(e,i,a){let{codecs:l}=a??{},u=n.createObserverHub(e=>i.getAll(e)),d=e=>{u.notify(e),a?.onMutation?.(e)},f=e=>{u.notify(e)},p=i.pruneAllExpired?.bind(i),m=a?.onCrossTabMessage?.(f)??void 0,h=c(l);a?.onTransactions?.({notifyMutation:d,validate:h});let g=new AbortController,_=!1,v=()=>{if(_)throw new t.VaultDisposedError},y={async clear(e){v(),await i.clear(e),d(e)},async count(e){return v(),i.count?i.count(e):(await i.getAll(e)).length},async delete(e,t){v();let n=await i.delete(e,t);return n&&d(e),n},async deleteMany(e,t){v();let n=i.deleteMany?await i.deleteMany(e,t):(await Promise.all(t.map(t=>i.delete(e,t)))).filter(Boolean).length;return n>0&&d(e),n},get disposalSignal(){return g.signal},async dispose(){_||(_=!0,g.abort(),m?.(),u.dispose(),await i.dispose?.())},get disposed(){return _},async get(e,t){return v(),i.get(e,t)},async getAll(e){return v(),i.getAll(e)},async getMany(e,t){return v(),i.getMany?i.getMany(e,t):Promise.all(t.map(t=>i.get(e,t)))},async has(e,t){return v(),i.has?i.has(e,t):await i.get(e,t)!==void 0},async isEmpty(e){return await y.count(e)===0},async keys(t,n){if(v(),!n&&i.getAllKeys)return i.getAllKeys(t);let r=await i.getAll(t),a=n?r.filter(n):r,o=e[t].key;return a.map(e=>e[o])},observe(e,t,n){return v(),u.observe(e,t,n)},async pruneExpired(){if(v(),p){let e=await p();for(let[t,n]of Object.entries(e))n>0&&d(t);return e}let t=Object.keys(e),n=await Promise.all(t.map(async e=>[e,await i.pruneExpiredInTable(e)]));for(let[e,t]of n)t>0&&d(e);return Object.fromEntries(n)},async put(t,n,r){v();let a=h(t,n);return await i.put(t,a,o(e,t,r)),d(t),a},async putAll(t,n,r){if(v(),n.length===0)return;let a=n.map(e=>h(t,e));await i.putAll(t,a,o(e,t,r)),d(t)},query(t){return v(),r.createQueryBuilder({deleteMany:async r=>{let i=r.map(r=>n.getRecordKey(e,t,r));return y.deleteMany(t,i)},source:()=>y.getAll(t)})},async update(t,n,r,i){let a=await y.get(t,n);if(a===void 0)return;let o={...a,...r};return s(e,t,n,o,`update`),y.put(t,o,i)},async upsert(t,n,r,i){let a=r(await y.get(t,n));return s(e,t,n,a,`upsert: apply()`),y.put(t,a,i)},async[Symbol.asyncDispose](){await y.dispose()}};return y}function d(e,n,r){let i=u(e,n,r);return Object.assign(i,{iterate(e){if(i.disposed)throw new t.VaultDisposedError;return n.iterate?n.iterate(e):(async function*(){for(let t of await i.getAll(e))yield t})()}})}function f(e,i,a){let o=(n,r)=>e.disposed?Promise.reject(new t.VaultDisposedError):i(n,r);return Object.assign(e,{batch:o,deleteMany(e,t){return o([e],n=>n.deleteMany(e,t))},query(i){if(e.disposed)throw new t.VaultDisposedError;return r.createQueryBuilder({deleteMany:async e=>{let t=e.map(e=>n.getRecordKey(a,i,e));return o([i],e=>e.deleteMany(i,t))},source:()=>e.getAll(i)})},update(e,t,n,r){return o([e],i=>i.update(e,t,n,r))},upsert(e,t,n,r){return o([e],i=>i.upsert(e,t,n,r))}})}async function p(e,t,n){return e.has(t,n)}async function m(e,t){return e.count(t)}async function h(e,t){return e.isEmpty(t)}async function g(e,t,n){return e.getMany(t,n)}async function _(e,t,n){return e.keys(t,n)}async function v(e,t,n){return e.deleteMany(t,n)}async function y(e,t,n,r,i){return e.update(t,n,r,i)}async function b(e,t,n,r,i){return e.upsert(t,n,r,i)}exports.assertBatchTables=a,exports.buildDocumentStore=d,exports.buildKeyValueStore=u,exports.buildTxContext=l,exports.count=m,exports.deleteMany=v,exports.getMany=g,exports.has=p,exports.isEmpty=h,exports.keys=_,exports.makeValidator=c,exports.update=y,exports.upsert=b,exports.withBatch=f;
1
+ const e=require("./errors.cjs"),t=require("./ttl.cjs"),n=require("./codec.cjs"),r=require("./internal.cjs");function assertBatchTables(t){if(t.length===0)throw new e.VaultError(`batch: declare at least one table`)}function batchScopeGuard(t,n=()=>!0){let r=new Set(t);return t=>{if(!n())throw new e.VaultScopeError(`transaction context is no longer active`);if(!r.has(t))throw new e.VaultScopeError(`table "${t}" is not part of this batch scope`)}}function resolveTtl(e,n,r){return r!==void 0&&t.assertPositiveFinite(r,`put/putAll`),r??e[n].defaultTtl}function verifyKey(t,n,i,a,o){let s=r.getRecordKey(t,n,a);if(s!==i)throw new e.VaultError(`${o}: key field "${t[n].key}" must be "${String(i)}" but got "${String(s)}" in table "${n}"`)}function makeValidator(t){return(r,i)=>{let a=t?.[r];if(!a)return i;try{return n.validateRecord(a,i)}catch(t){throw new e.VaultError(`validation failed for table "${r}"`,{cause:t})}}}function buildOperations(e,t,n,r,i){let a={async clear(e){i(e),await t.clear(e),n(e)},async count(e){return i(e),(await t.getAll(e)).length},async delete(e,r){i(e);let a=await t.delete(e,r);return a&&n(e),a},async deleteMany(e,r){i(e);let a=t.deleteMany?await t.deleteMany(e,r):(await Promise.all(r.map(n=>t.delete(e,n)))).filter(Boolean).length;return a>0&&n(e),a},async get(e,n){return i(e),t.get(e,n)},async getAll(e){return i(e),t.getAll(e)},async getMany(e,n){return i(e),t.getMany?t.getMany(e,n):Promise.all(n.map(n=>t.get(e,n)))},async has(e,n){return i(e),await t.get(e,n)!==void 0},async isEmpty(e){return await a.count(e)===0},iterate(e){return i(e),t.iterate?t.iterate(e):(async function*(){for(let n of await t.getAll(e))yield n})()},async keys(n,r){i(n);let a=await t.getAll(n),o=r?a.filter(r):a,s=e[n].key;return o.map(e=>e[s])},async put(a,o,s){i(a);let c=r(a,o);return await t.put(a,c,resolveTtl(e,a,s)),n(a),c},async putAll(a,o,s){if(i(a),o.length===0)return;let c=o.map(e=>r(a,e));await t.putAll(a,c,resolveTtl(e,a,s)),n(a)},async update(t,n,r,i){let o=await a.get(t,n);if(o===void 0)return;let s={...o,...r};return verifyKey(e,t,n,s,`update`),a.put(t,s,i)},async upsert(t,n,r,i){let o=r(await a.get(t,n));return verifyKey(e,t,n,o,`upsert: apply()`),a.put(t,o,i)}};return a}function buildStoreCore(t,n,i){let a=r.createObserverHub(e=>n.getAll(e)),notifyMutation=e=>{a.notify(e),i.onMutation?.(e)},notifyExternal=e=>{a.notify(e)},o=i.onCrossTabMessage?.(notifyExternal)??void 0,s=makeValidator(i.codecs),c=n.pruneAllExpired?.bind(n),l=new AbortController,u=!1,checkDisposed=()=>{if(u)throw new e.VaultDisposedError},d={...buildOperations(t,n,notifyMutation,s,()=>checkDisposed()),get disposalSignal(){return l.signal},async dispose(){u||(u=!0,l.abort(),o?.(),a.dispose(),await n.dispose?.())},get disposed(){return u},observe(e,t,n){return checkDisposed(),a.observe(e,t,n)},async pruneExpired(){if(checkDisposed(),c){let e=await c();for(let[t,n]of Object.entries(e))n>0&&notifyMutation(t);return e}let e=Object.keys(t),r=await Promise.all(e.map(async e=>[e,await n.pruneExpiredInTable(e)]));for(let[e,t]of r)t>0&&notifyMutation(e);return Object.fromEntries(r)},async[Symbol.asyncDispose](){await d.dispose()}};return{disposed:()=>u,notifyMutation,store:d,validate:s}}function buildVaultStore(e,t,n={schema:e}){return buildStoreCore(e,t,n).store}function buildDocumentStore(t,n,r){let{disposed:i,notifyMutation:a,store:o,validate:s}=buildStoreCore(t,n,r),c=r.createBatch({notifyMutation:a,validate:s}),runBatch=(t,n)=>i()?Promise.reject(new e.VaultDisposedError):c(t,n);return Object.assign(o,{batch:runBatch,deleteMany(e,t){return runBatch([e],n=>n.deleteMany(e,t))},update(e,t,n,r){return runBatch([e],i=>i.update(e,t,n,r))},upsert(e,t,n,r){return runBatch([e],i=>i.upsert(e,t,n,r))}})}exports.assertBatchTables=assertBatchTables,exports.batchScopeGuard=batchScopeGuard,exports.buildDocumentStore=buildDocumentStore,exports.buildOperations=buildOperations,exports.buildVaultStore=buildVaultStore,exports.makeValidator=makeValidator;
2
2
  //# sourceMappingURL=adapter-core.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.cjs","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { validateRecord } from './codec';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder } from './query';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n DocumentVaultStore,\n DurableStoreOptions,\n KeyOf,\n KeyValueVaultStore,\n MemoryStoreOptions,\n RecordOf,\n TableCodecs,\n TransactionContext,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/** @internal Full backend protocol implemented by each adapter. */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n count?<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n dispose?(): Promise<void>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n getAllKeys?<T extends K>(table: T): Promise<KeyOf<S, T>[]>;\n getMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has?<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n /**\n * Optional: lazy iteration over live records. Document stores implement this natively\n * (IDB cursor, SQLite keyset pagination). Key-value stores fall back to getAll().\n */\n iterate?<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n /**\n * Optional: prune all expired records across all tables in a single atomic operation.\n * When present, `pruneExpired()` delegates here instead of calling `pruneExpiredInTable` N times.\n */\n pruneAllExpired?(): Promise<Record<string, number>>;\n pruneExpiredInTable<T extends K>(table: T): Promise<number>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** @internal */\nexport type BatchDeps<S extends AnySchema> = {\n notifyMutation: (table: keyof S & string) => void;\n validate: <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;\n};\n\n/** @internal */\nexport type BatchImpl<S extends AnySchema> = <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n) => Promise<R>;\n\n/* -------------------- Helpers -------------------- */\n\nexport function assertBatchTables(tables: readonly string[]): void {\n if (tables.length === 0) throw new VaultError('batch: declare at least one table');\n}\n\nfunction resolveTtl<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n ttl?: number,\n): number | undefined {\n if (ttl !== undefined) assertPositiveFinite(ttl, 'put/putAll');\n\n return ttl ?? schema[table].defaultTtl;\n}\n\nfunction verifyKey<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n expected: KeyOf<S, K>,\n value: RecordOf<S, K>,\n op: string,\n): void {\n const actual = getRecordKey(schema, table, value);\n\n if (actual !== expected) {\n throw new VaultError(\n `${op}: key field \"${schema[table].key}\" must be \"${String(expected)}\" but got \"${String(actual)}\" in table \"${table}\"`,\n );\n }\n}\n\n/* -------------------- Codec helpers -------------------- */\n\nexport function makeValidator<S extends AnySchema>(\n codecs: TableCodecs<S> | undefined,\n): <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K> {\n return <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const codec = codecs?.[table];\n\n if (!codec) return value;\n\n try {\n return validateRecord(codec, value);\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n}\n\n/* -------------------- buildTxContext -------------------- */\n\nexport function buildTxContext<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n core: StorageBackend<S, K>,\n onMutate: (table: K) => void,\n validate?: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n scope?: ReadonlySet<string>,\n isActive: () => boolean = () => true,\n): TransactionContext<S, K> {\n const applyValidation = validate ?? ((_: K, v: RecordOf<S, K>) => v);\n\n const checkScope = (table: K): void => {\n if (!isActive()) throw new VaultScopeError('transaction context is no longer active');\n if (scope && !scope.has(table)) throw new VaultScopeError(`table \"${table}\" is not part of this batch scope`);\n };\n\n const context: TransactionContext<S, K> = {\n async clear(table) {\n checkScope(table);\n await core.clear(table);\n onMutate(table);\n },\n async count(table) {\n checkScope(table);\n return core.count ? core.count(table) : (await core.getAll(table)).length;\n },\n async delete(table, key) {\n checkScope(table);\n\n const deleted = await core.delete(table, key);\n\n if (deleted) onMutate(table);\n\n return deleted;\n },\n async deleteMany(table, keys) {\n checkScope(table);\n if (!core.deleteMany) {\n const results = await Promise.all(keys.map((key) => core.delete(table, key)));\n const deleted = results.filter(Boolean).length;\n if (deleted > 0) onMutate(table);\n return deleted;\n }\n const deleted = await core.deleteMany(table, keys);\n if (deleted > 0) onMutate(table);\n return deleted;\n },\n async get(table, key) {\n checkScope(table);\n\n return core.get(table, key);\n },\n async getAll(table) {\n checkScope(table);\n\n return core.getAll(table);\n },\n async getMany(table, keys) {\n checkScope(table);\n return core.getMany ? core.getMany(table, keys) : Promise.all(keys.map((key) => core.get(table, key)));\n },\n async has(table, key) {\n checkScope(table);\n return core.has ? core.has(table, key) : (await core.get(table, key)) !== undefined;\n },\n async isEmpty(table) {\n return (await context.count(table)) === 0;\n },\n iterate(table) {\n checkScope(table);\n\n if (core.iterate) return core.iterate(table);\n\n // Fallback: iterate over getAll() materialized records.\n return (async function* (): AsyncIterable<RecordOf<S, typeof table>> {\n const records = await core.getAll(table);\n\n for (const record of records) yield record;\n })();\n },\n async keys(table, filter) {\n checkScope(table);\n if (!filter && core.getAllKeys) return core.getAllKeys(table);\n const records = await core.getAll(table);\n const selected = filter ? records.filter(filter) : records;\n const keyField = schema[table].key;\n return selected.map((record) => (record as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n checkScope(table);\n const canonical = applyValidation(table, value);\n await core.put(table, canonical, resolveTtl(schema, table, ttl));\n onMutate(table);\n return canonical;\n },\n async putAll(table, values, ttl) {\n checkScope(table);\n\n if (values.length === 0) return;\n\n const toWrite = values.map((v) => applyValidation(table, v));\n\n await core.putAll(table, toWrite, resolveTtl(schema, table, ttl));\n onMutate(table);\n },\n query(table) {\n checkScope(table);\n return createQueryBuilder({\n deleteMany: async (records) => {\n const keys = records.map((record) => getRecordKey(schema, table, record));\n return context.deleteMany(table, keys);\n },\n source: () => context.getAll(table),\n });\n },\n async update(table, key, changes, ttl) {\n const current = await context.get(table, key);\n if (current === undefined) return undefined;\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n verifyKey(schema, table, key, merged, 'update');\n return context.put(table, merged, ttl);\n },\n async upsert(table, key, update, ttl) {\n const value = update(await context.get(table, key));\n verifyKey(schema, table, key, value, 'upsert: update()');\n return context.put(table, value, ttl);\n },\n };\n\n return context;\n}\n\n/* -------------------- buildKeyValueStore -------------------- */\n\n/** Builds a KeyValueVaultStore from a backend and options. */\nexport function buildKeyValueStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: MemoryStoreOptions<S> & {\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n onTransactions?: (deps: BatchDeps<S>) => void;\n },\n): KeyValueVaultStore<S> {\n const { codecs } = options ?? {};\n\n const observers = createObserverHub<S>((table) => core.getAll(table));\n\n const notifyMutation = (table: keyof S & string): void => {\n observers.notify(table);\n options?.onMutation?.(table);\n };\n\n // Cross-tab notifications must NOT call onMutation (which would re-publish to BroadcastChannel,\n // creating an infinite loop). They only notify local observers.\n const notifyExternal = (table: keyof S & string): void => {\n observers.notify(table);\n };\n\n const pruneAll = core.pruneAllExpired?.bind(core);\n\n const disconnectExternal = options?.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = makeValidator(codecs);\n\n options?.onTransactions?.({ notifyMutation, validate });\n\n const disposeController = new AbortController();\n let disposed = false;\n\n const checkDisposed = (): void => {\n if (disposed) throw new VaultDisposedError();\n };\n\n const adapter: KeyValueVaultStore<S> = {\n async clear(table) {\n checkDisposed();\n await core.clear(table);\n notifyMutation(table);\n },\n async count(table) {\n checkDisposed();\n return core.count ? core.count(table) : (await core.getAll(table)).length;\n },\n\n async delete(table, key) {\n checkDisposed();\n\n const deleted = await core.delete(table, key);\n\n if (deleted) notifyMutation(table);\n\n return deleted;\n },\n async deleteMany(table, keys) {\n checkDisposed();\n const deleted = core.deleteMany\n ? await core.deleteMany(table, keys)\n : (await Promise.all(keys.map((key) => core.delete(table, key)))).filter(Boolean).length;\n if (deleted > 0) notifyMutation(table);\n return deleted;\n },\n\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n async dispose() {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n disconnectExternal?.();\n observers.dispose();\n await core.dispose?.();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n async get(table, key) {\n checkDisposed();\n\n return core.get(table, key);\n },\n\n async getAll(table) {\n checkDisposed();\n\n return core.getAll(table);\n },\n async getMany(table, keys) {\n checkDisposed();\n return core.getMany ? core.getMany(table, keys) : Promise.all(keys.map((key) => core.get(table, key)));\n },\n async has(table, key) {\n checkDisposed();\n return core.has ? core.has(table, key) : (await core.get(table, key)) !== undefined;\n },\n async isEmpty(table) {\n return (await adapter.count(table)) === 0;\n },\n async keys(table, filter) {\n checkDisposed();\n if (!filter && core.getAllKeys) return core.getAllKeys(table);\n const records = await core.getAll(table);\n const selected = filter ? records.filter(filter) : records;\n const keyField = schema[table].key;\n return selected.map((record) => (record as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n\n observe(table, listener, opts) {\n checkDisposed();\n\n return observers.observe(table, listener, opts);\n },\n\n async pruneExpired() {\n checkDisposed();\n\n if (pruneAll) {\n const result = await pruneAll();\n\n for (const [table, pruned] of Object.entries(result)) {\n if (pruned > 0) notifyMutation(table as keyof S & string);\n }\n\n return result as { [K in keyof S & string]: number };\n }\n\n const tableNames = Object.keys(schema) as Array<keyof S & string>;\n const pairs = await Promise.all(\n tableNames.map(async (name) => {\n const pruned = await core.pruneExpiredInTable(name);\n\n return [name, pruned] as const;\n }),\n );\n\n for (const [table, pruned] of pairs) {\n if (pruned > 0) notifyMutation(table);\n }\n\n return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async put(table, value, ttl) {\n checkDisposed();\n const canonical = validate(table, value);\n await core.put(table, canonical, resolveTtl(schema, table, ttl));\n notifyMutation(table);\n return canonical;\n },\n\n async putAll(table, values, ttl) {\n checkDisposed();\n\n if (values.length === 0) return;\n\n const toWrite = values.map((v) => validate(table, v));\n\n await core.putAll(table, toWrite, resolveTtl(schema, table, ttl));\n notifyMutation(table);\n },\n query(table) {\n checkDisposed();\n return createQueryBuilder({\n deleteMany: async (records) => {\n const keys = records.map((record) => getRecordKey(schema, table, record));\n return adapter.deleteMany(table, keys);\n },\n source: () => adapter.getAll(table),\n });\n },\n async update(table, key, changes, ttl) {\n const current = await adapter.get(table, key);\n if (current === undefined) return undefined;\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n verifyKey(schema, table, key, merged, 'update');\n return adapter.put(table, merged, ttl);\n },\n async upsert(table, key, apply, ttl) {\n const value = apply(await adapter.get(table, key));\n verifyKey(schema, table, key, value, 'upsert: apply()');\n return adapter.put(table, value, ttl);\n },\n\n async [Symbol.asyncDispose]() {\n await adapter.dispose();\n },\n };\n\n return adapter;\n}\n\n/* -------------------- buildDocumentStore -------------------- */\n\n/** A document store before batch() is attached. */\nexport type DocumentVaultStoreWithoutBatch<S extends AnySchema> = Omit<DocumentVaultStore<S>, 'batch'>;\n\n/** Builds a DocumentVaultStore (without batch) from a backend, codecs, and options. */\nexport function buildDocumentStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: DurableStoreOptions<S> & {\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n onTransactions?: (deps: BatchDeps<S>) => void;\n },\n): DocumentVaultStoreWithoutBatch<S> {\n const store = buildKeyValueStore(schema, core, options);\n\n return Object.assign(store, {\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (store.disposed) throw new VaultDisposedError();\n if (core.iterate) return core.iterate(table);\n return (async function* (): AsyncIterable<RecordOf<S, K>> {\n for (const record of await store.getAll(table)) yield record;\n })();\n },\n });\n}\n\n/** Attaches a batch() implementation to a document store. */\nexport function withBatch<S extends AnySchema>(\n store: DocumentVaultStoreWithoutBatch<S>,\n batch: BatchImpl<S>,\n schema: S,\n): DocumentVaultStore<S> {\n const runBatch: BatchImpl<S> = (tables, fn) => {\n if (store.disposed) return Promise.reject(new VaultDisposedError());\n return batch(tables, fn);\n };\n\n return Object.assign(store, {\n batch: runBatch,\n deleteMany<K extends keyof S & string>(table: K, keys: readonly KeyOf<S, K>[]) {\n return runBatch([table], (tx) => tx.deleteMany(table, keys));\n },\n query<K extends keyof S & string>(table: K) {\n if (store.disposed) throw new VaultDisposedError();\n return createQueryBuilder({\n deleteMany: async (records) => {\n const keys = records.map((record) => getRecordKey(schema, table, record));\n return runBatch([table], (tx) => tx.deleteMany(table, keys));\n },\n source: () => store.getAll(table),\n });\n },\n update<K extends keyof S & string>(table: K, key: KeyOf<S, K>, changes: Partial<RecordOf<S, K>>, ttl?: number) {\n return runBatch([table], (tx) => tx.update(table, key, changes, ttl));\n },\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ) {\n return runBatch([table], (tx) => tx.upsert(table, key, apply, ttl));\n },\n }) as DocumentVaultStore<S>;\n}\n\n/* -------------------- Derived convenience helpers -------------------- */\n\n/** Check whether a record exists for the given key. */\nexport async function has<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n key: KeyOf<S, K>,\n): Promise<boolean> {\n return store.has(table, key);\n}\n\n/** Count live records in a table. */\nexport async function count<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n): Promise<number> {\n return store.count(table);\n}\n\n/** Check whether a table has no live records. */\nexport async function isEmpty<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n): Promise<boolean> {\n return store.isEmpty(table);\n}\n\n/** Fetch multiple records by key. Preserves key order; missing keys yield `undefined`. */\nexport async function getMany<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n keys: readonly KeyOf<S, K>[],\n): Promise<Array<RecordOf<S, K> | undefined>> {\n return store.getMany(table, keys);\n}\n\n/** Fetch all live primary keys for a table. */\nexport async function keys<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n filter?: (record: RecordOf<S, K>) => boolean,\n): Promise<KeyOf<S, K>[]> {\n return store.keys(table, filter);\n}\n\n/** Delete multiple records by key. Returns the count of records that were actually deleted. */\nexport async function deleteMany<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n keys: readonly KeyOf<S, K>[],\n): Promise<number> {\n return store.deleteMany(table, keys);\n}\n\n/** Partially update an existing record. Returns `undefined` when the key does not exist. */\nexport async function update<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n): Promise<RecordOf<S, K> | undefined> {\n return store.update(table, key, changes, ttl);\n}\n\n/** Read-modify-write: the callback receives the current record (or `undefined`) and returns the value to store. */\nexport async function upsert<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n key: KeyOf<S, K>,\n apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n): Promise<RecordOf<S, K>> {\n return store.upsert(table, key, apply, ttl);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"adapter-core.cjs","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { validateRecord } from './codec';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n DocumentVaultStore,\n KeyOf,\n KeyValueVaultStore,\n MemoryStoreOptions,\n RecordOf,\n TableCodecs,\n TransactionContext,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/**\n * @internal Backend protocol implemented by each adapter. Only operations an\n * adapter can perform natively appear here; every convenience is layered on\n * once in `buildOperations`.\n */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n /** Native multi-key delete in one transaction (IndexedDB). Falls back to per-key deletes. */\n deleteMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n dispose?(): Promise<void>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n /** Native multi-key read in one transaction (IndexedDB). Falls back to per-key reads. */\n getMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n /** Lazy iteration over live records. Adapters without native cursors fall back to `getAll()`. */\n iterate?<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n /** Prune expired records across all tables in a single operation. */\n pruneAllExpired?(): Promise<Record<string, number>>;\n pruneExpiredInTable<T extends K>(table: T): Promise<number>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** @internal Capabilities a batch factory needs from the surrounding store. */\nexport type BatchDeps<S extends AnySchema> = {\n notifyMutation: (table: keyof S & string) => void;\n validate: <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;\n};\n\n/** @internal */\nexport type BatchImpl<S extends AnySchema> = <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n) => Promise<R>;\n\n/* -------------------- Helpers -------------------- */\n\nexport function assertBatchTables(tables: readonly string[]): void {\n if (tables.length === 0) throw new VaultError('batch: declare at least one table');\n}\n\n/**\n * @internal Guard for transaction contexts: rejects use after the batch settled\n * and access to tables outside the declared scope.\n */\nexport function batchScopeGuard(\n tables: readonly string[],\n isActive: () => boolean = () => true,\n): (table: string) => void {\n const scope = new Set(tables);\n\n return (table): void => {\n if (!isActive()) throw new VaultScopeError('transaction context is no longer active');\n if (!scope.has(table)) throw new VaultScopeError(`table \"${table}\" is not part of this batch scope`);\n };\n}\n\nfunction resolveTtl<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n ttl?: number,\n): number | undefined {\n if (ttl !== undefined) assertPositiveFinite(ttl, 'put/putAll');\n\n return ttl ?? schema[table].defaultTtl;\n}\n\nfunction verifyKey<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n expected: KeyOf<S, K>,\n value: RecordOf<S, K>,\n op: string,\n): void {\n const actual = getRecordKey(schema, table, value);\n\n if (actual !== expected) {\n throw new VaultError(\n `${op}: key field \"${schema[table].key}\" must be \"${String(expected)}\" but got \"${String(actual)}\" in table \"${table}\"`,\n );\n }\n}\n\n/* -------------------- Codec helpers -------------------- */\n\nexport function makeValidator<S extends AnySchema>(\n codecs: TableCodecs<S> | undefined,\n): <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K> {\n return <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const codec = codecs?.[table];\n\n if (!codec) return value;\n\n try {\n return validateRecord(codec, value);\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n}\n\n/* -------------------- buildOperations -------------------- */\n\n/**\n * @internal The single implementation of every store and transaction operation.\n * A public store and a batch transaction context differ only in who learns\n * about a mutation and which guard rejects illegal use, so both are built here.\n */\nexport function buildOperations<S extends AnySchema, K extends keyof S & string = keyof S & string>(\n schema: S,\n core: StorageBackend<S, K>,\n notify: (table: K) => void,\n validate: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n guard: (table: K) => void,\n): TransactionContext<S, K> {\n const ops: TransactionContext<S, K> = {\n async clear(table) {\n guard(table);\n await core.clear(table);\n notify(table);\n },\n async count(table) {\n guard(table);\n\n return (await core.getAll(table)).length;\n },\n async delete(table, key) {\n guard(table);\n\n const deleted = await core.delete(table, key);\n\n if (deleted) notify(table);\n\n return deleted;\n },\n async deleteMany(table, keys) {\n guard(table);\n\n const deleted = core.deleteMany\n ? await core.deleteMany(table, keys)\n : (await Promise.all(keys.map((key) => core.delete(table, key)))).filter(Boolean).length;\n\n if (deleted > 0) notify(table);\n\n return deleted;\n },\n async get(table, key) {\n guard(table);\n\n return core.get(table, key);\n },\n async getAll(table) {\n guard(table);\n\n return core.getAll(table);\n },\n async getMany(table, keys) {\n guard(table);\n\n return core.getMany ? core.getMany(table, keys) : Promise.all(keys.map((key) => core.get(table, key)));\n },\n async has(table, key) {\n guard(table);\n\n return (await core.get(table, key)) !== undefined;\n },\n async isEmpty(table) {\n return (await ops.count(table)) === 0;\n },\n iterate(table) {\n guard(table);\n\n if (core.iterate) return core.iterate(table);\n\n return (async function* (): AsyncIterable<RecordOf<S, typeof table>> {\n for (const record of await core.getAll(table)) yield record;\n })();\n },\n async keys(table, filter) {\n guard(table);\n\n const records = await core.getAll(table);\n const selected = filter ? records.filter(filter) : records;\n const keyField = schema[table].key;\n\n return selected.map((record) => (record as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n guard(table);\n\n const canonical = validate(table, value);\n\n await core.put(table, canonical, resolveTtl(schema, table, ttl));\n notify(table);\n\n return canonical;\n },\n async putAll(table, values, ttl) {\n guard(table);\n\n if (values.length === 0) return;\n\n const toWrite = values.map((v) => validate(table, v));\n\n await core.putAll(table, toWrite, resolveTtl(schema, table, ttl));\n notify(table);\n },\n async update(table, key, changes, ttl) {\n const current = await ops.get(table, key);\n\n if (current === undefined) return undefined;\n\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n\n verifyKey(schema, table, key, merged, 'update');\n\n return ops.put(table, merged, ttl);\n },\n async upsert(table, key, apply, ttl) {\n const value = apply(await ops.get(table, key));\n\n verifyKey(schema, table, key, value, 'upsert: apply()');\n\n return ops.put(table, value, ttl);\n },\n };\n\n return ops;\n}\n\n/* -------------------- buildVaultStore -------------------- */\n\n/** @internal Options shared by every store builder. */\nexport type VaultStoreOptions<S extends AnySchema> = MemoryStoreOptions<S> & {\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n};\n\n/** @internal Options for document stores, which supply their own batch factory. */\nexport type DocumentStoreOptions<S extends AnySchema> = VaultStoreOptions<S> & {\n createBatch: (deps: BatchDeps<S>) => BatchImpl<S>;\n};\n\ntype StoreCore<S extends AnySchema> = {\n disposed: () => boolean;\n notifyMutation: (table: keyof S & string) => void;\n store: KeyValueVaultStore<S>;\n validate: <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;\n};\n\nfunction buildStoreCore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: VaultStoreOptions<S>,\n): StoreCore<S> {\n const observers = createObserverHub<S>((table) => core.getAll(table));\n\n const notifyMutation = (table: keyof S & string): void => {\n observers.notify(table);\n options.onMutation?.(table);\n };\n\n // Cross-tab notifications must NOT call onMutation (which would re-publish to\n // BroadcastChannel and create an infinite loop). They only notify local observers.\n const notifyExternal = (table: keyof S & string): void => {\n observers.notify(table);\n };\n\n const disconnectExternal = options.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = makeValidator(options.codecs);\n const pruneAll = core.pruneAllExpired?.bind(core);\n\n const disposeController = new AbortController();\n let disposed = false;\n\n const checkDisposed = (): void => {\n if (disposed) throw new VaultDisposedError();\n };\n\n const ops = buildOperations<S>(schema, core, notifyMutation, validate, () => checkDisposed());\n\n const store: KeyValueVaultStore<S> = {\n ...ops,\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n async dispose() {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n disconnectExternal?.();\n observers.dispose();\n await core.dispose?.();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n observe(table, listener, opts) {\n checkDisposed();\n\n return observers.observe(table, listener, opts);\n },\n\n async pruneExpired() {\n checkDisposed();\n\n if (pruneAll) {\n const result = await pruneAll();\n\n for (const [table, pruned] of Object.entries(result)) {\n if (pruned > 0) notifyMutation(table as keyof S & string);\n }\n\n return result as { [K in keyof S & string]: number };\n }\n\n const tableNames = Object.keys(schema) as Array<keyof S & string>;\n const pairs = await Promise.all(\n tableNames.map(async (name) => {\n const pruned = await core.pruneExpiredInTable(name);\n\n return [name, pruned] as const;\n }),\n );\n\n for (const [table, pruned] of pairs) {\n if (pruned > 0) notifyMutation(table);\n }\n\n return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async [Symbol.asyncDispose]() {\n await store.dispose();\n },\n };\n\n return { disposed: () => disposed, notifyMutation, store, validate };\n}\n\n/** Builds a key-value store: core CRUD, observation, TTL, and pruning. */\nexport function buildVaultStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: VaultStoreOptions<S> = { schema },\n): KeyValueVaultStore<S> {\n return buildStoreCore(schema, core, options).store;\n}\n\n/** Builds a document store: the key-value surface plus atomic `batch()`. */\nexport function buildDocumentStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: DocumentStoreOptions<S>,\n): DocumentVaultStore<S> {\n const { disposed, notifyMutation, store, validate } = buildStoreCore(schema, core, options);\n const batch = options.createBatch({ notifyMutation, validate });\n\n const runBatch = <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R> => {\n if (disposed()) return Promise.reject(new VaultDisposedError());\n\n return batch(tables, fn);\n };\n\n // Read-modify-write helpers stay atomic on document stores by running inside a\n // single-table batch instead of the plain read-then-write pair.\n return Object.assign(store, {\n batch: runBatch,\n deleteMany<K extends keyof S & string>(table: K, keys: readonly KeyOf<S, K>[]): Promise<number> {\n return runBatch([table], (tx) => tx.deleteMany(table, keys));\n },\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined> {\n return runBatch([table], (tx) => tx.update(table, key, changes, ttl));\n },\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>> {\n return runBatch([table], (tx) => tx.upsert(table, key, apply, ttl));\n },\n }) as DocumentVaultStore<S>;\n}\n"],"mappings":"4GAuDA,SAAgB,kBAAkB,EAAiC,CACjE,GAAI,EAAO,SAAW,EAAG,MAAM,IAAI,EAAA,WAAW,mCAAmC,CACnF,CAMA,SAAgB,gBACd,EACA,MAAgC,GACP,CACzB,IAAM,EAAQ,IAAI,IAAI,CAAM,EAE5B,MAAQ,IAAgB,CACtB,GAAI,CAAC,EAAS,EAAG,MAAM,IAAI,EAAA,gBAAgB,yCAAyC,EACpF,GAAI,CAAC,EAAM,IAAI,CAAK,EAAG,MAAM,IAAI,EAAA,gBAAgB,UAAU,EAAM,kCAAkC,CACrG,CACF,CAEA,SAAS,WACP,EACA,EACA,EACoB,CAGpB,OAFI,IAAQ,IAAA,IAAW,EAAA,qBAAqB,EAAK,YAAY,EAEtD,GAAO,EAAO,EAAM,CAAC,UAC9B,CAEA,SAAS,UACP,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EAAS,EAAA,aAAa,EAAQ,EAAO,CAAK,EAEhD,GAAI,IAAW,EACb,MAAM,IAAI,EAAA,WACR,GAAG,EAAG,eAAe,EAAO,EAAM,CAAC,IAAI,aAAa,OAAO,CAAQ,EAAE,aAAa,OAAO,CAAM,EAAE,cAAc,EAAM,EACvH,CAEJ,CAIA,SAAgB,cACd,EACiF,CACjF,OAAoC,EAAU,IAA0C,CACtF,IAAM,EAAQ,IAAS,GAEvB,GAAI,CAAC,EAAO,OAAO,EAEnB,GAAI,CACF,OAAO,EAAA,eAAe,EAAO,CAAK,CACpC,OAAS,EAAK,CACZ,MAAM,IAAI,EAAA,WAAW,gCAAgC,EAAM,GAAI,CAAE,MAAO,CAAI,CAAC,CAC/E,CACF,CACF,CASA,SAAgB,gBACd,EACA,EACA,EACA,EACA,EAC0B,CAC1B,IAAM,EAAgC,CACpC,MAAM,MAAM,EAAO,CACjB,EAAM,CAAK,EACX,MAAM,EAAK,MAAM,CAAK,EACtB,EAAO,CAAK,CACd,EACA,MAAM,MAAM,EAAO,CAGjB,OAFA,EAAM,CAAK,GAEH,MAAM,EAAK,OAAO,CAAK,EAAA,CAAG,MACpC,EACA,MAAM,OAAO,EAAO,EAAK,CACvB,EAAM,CAAK,EAEX,IAAM,EAAU,MAAM,EAAK,OAAO,EAAO,CAAG,EAI5C,OAFI,GAAS,EAAO,CAAK,EAElB,CACT,EACA,MAAM,WAAW,EAAO,EAAM,CAC5B,EAAM,CAAK,EAEX,IAAM,EAAU,EAAK,WACjB,MAAM,EAAK,WAAW,EAAO,CAAI,GAChC,MAAM,QAAQ,IAAI,EAAK,IAAK,GAAQ,EAAK,OAAO,EAAO,CAAG,CAAC,CAAC,EAAA,CAAG,OAAO,OAAO,CAAC,CAAC,OAIpF,OAFI,EAAU,GAAG,EAAO,CAAK,EAEtB,CACT,EACA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAM,CAAK,EAEJ,EAAK,IAAI,EAAO,CAAG,CAC5B,EACA,MAAM,OAAO,EAAO,CAGlB,OAFA,EAAM,CAAK,EAEJ,EAAK,OAAO,CAAK,CAC1B,EACA,MAAM,QAAQ,EAAO,EAAM,CAGzB,OAFA,EAAM,CAAK,EAEJ,EAAK,QAAU,EAAK,QAAQ,EAAO,CAAI,EAAI,QAAQ,IAAI,EAAK,IAAK,GAAQ,EAAK,IAAI,EAAO,CAAG,CAAC,CAAC,CACvG,EACA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAM,CAAK,EAEH,MAAM,EAAK,IAAI,EAAO,CAAG,IAAO,IAAA,EAC1C,EACA,MAAM,QAAQ,EAAO,CACnB,OAAQ,MAAM,EAAI,MAAM,CAAK,IAAO,CACtC,EACA,QAAQ,EAAO,CAKb,OAJA,EAAM,CAAK,EAEP,EAAK,QAAgB,EAAK,QAAQ,CAAK,GAEnC,iBAA6D,CACnE,IAAK,IAAM,KAAU,MAAM,EAAK,OAAO,CAAK,EAAG,MAAM,CACvD,EAAA,CAAG,CACL,EACA,MAAM,KAAK,EAAO,EAAQ,CACxB,EAAM,CAAK,EAEX,IAAM,EAAU,MAAM,EAAK,OAAO,CAAK,EACjC,EAAW,EAAS,EAAQ,OAAO,CAAM,EAAI,EAC7C,EAAW,EAAO,EAAM,CAAC,IAE/B,OAAO,EAAS,IAAK,GAAY,EAAmC,EAAmC,CACzG,EACA,MAAM,IAAI,EAAO,EAAO,EAAK,CAC3B,EAAM,CAAK,EAEX,IAAM,EAAY,EAAS,EAAO,CAAK,EAKvC,OAHA,MAAM,EAAK,IAAI,EAAO,EAAW,WAAW,EAAQ,EAAO,CAAG,CAAC,EAC/D,EAAO,CAAK,EAEL,CACT,EACA,MAAM,OAAO,EAAO,EAAQ,EAAK,CAG/B,GAFA,EAAM,CAAK,EAEP,EAAO,SAAW,EAAG,OAEzB,IAAM,EAAU,EAAO,IAAK,GAAM,EAAS,EAAO,CAAC,CAAC,EAEpD,MAAM,EAAK,OAAO,EAAO,EAAS,WAAW,EAAQ,EAAO,CAAG,CAAC,EAChE,EAAO,CAAK,CACd,EACA,MAAM,OAAO,EAAO,EAAK,EAAS,EAAK,CACrC,IAAM,EAAU,MAAM,EAAI,IAAI,EAAO,CAAG,EAExC,GAAI,IAAY,IAAA,GAAW,OAE3B,IAAM,EAAS,CAAE,GAAG,EAAS,GAAG,CAAQ,EAIxC,OAFA,UAAU,EAAQ,EAAO,EAAK,EAAQ,QAAQ,EAEvC,EAAI,IAAI,EAAO,EAAQ,CAAG,CACnC,EACA,MAAM,OAAO,EAAO,EAAK,EAAO,EAAK,CACnC,IAAM,EAAQ,EAAM,MAAM,EAAI,IAAI,EAAO,CAAG,CAAC,EAI7C,OAFA,UAAU,EAAQ,EAAO,EAAK,EAAO,iBAAiB,EAE/C,EAAI,IAAI,EAAO,EAAO,CAAG,CAClC,CACF,EAEA,OAAO,CACT,CAsBA,SAAS,eACP,EACA,EACA,EACc,CACd,IAAM,EAAY,EAAA,kBAAsB,GAAU,EAAK,OAAO,CAAK,CAAC,EAE9D,eAAkB,GAAkC,CACxD,EAAU,OAAO,CAAK,EACtB,EAAQ,aAAa,CAAK,CAC5B,EAIM,eAAkB,GAAkC,CACxD,EAAU,OAAO,CAAK,CACxB,EAEM,EAAqB,EAAQ,oBAAoB,cAAc,GAAK,IAAA,GAEpE,EAAW,cAAc,EAAQ,MAAM,EACvC,EAAW,EAAK,iBAAiB,KAAK,CAAI,EAE1C,EAAoB,IAAI,gBAC1B,EAAW,GAET,kBAA4B,CAChC,GAAI,EAAU,MAAM,IAAI,EAAA,kBAC1B,EAIM,EAA+B,CACnC,GAHU,gBAAmB,EAAQ,EAAM,eAAgB,MAAgB,cAAc,CAGtF,EACH,IAAI,gBAA8B,CAChC,OAAO,EAAkB,MAC3B,EAEA,MAAM,SAAU,CACV,IAEJ,EAAW,GACX,EAAkB,MAAM,EACxB,IAAqB,EACrB,EAAU,QAAQ,EAClB,MAAM,EAAK,UAAU,EACvB,EAEA,IAAI,UAAoB,CACtB,OAAO,CACT,EAEA,QAAQ,EAAO,EAAU,EAAM,CAG7B,OAFA,cAAc,EAEP,EAAU,QAAQ,EAAO,EAAU,CAAI,CAChD,EAEA,MAAM,cAAe,CAGnB,GAFA,cAAc,EAEV,EAAU,CACZ,IAAM,EAAS,MAAM,EAAS,EAE9B,IAAK,GAAM,CAAC,EAAO,KAAW,OAAO,QAAQ,CAAM,EAC7C,EAAS,GAAG,eAAe,CAAyB,EAG1D,OAAO,CACT,CAEA,IAAM,EAAa,OAAO,KAAK,CAAM,EAC/B,EAAQ,MAAM,QAAQ,IAC1B,EAAW,IAAI,KAAO,IAGb,CAAC,EAAM,MAFO,EAAK,oBAAoB,CAAI,CAE9B,CACrB,CACH,EAEA,IAAK,GAAM,CAAC,EAAO,KAAW,EACxB,EAAS,GAAG,eAAe,CAAK,EAGtC,OAAO,OAAO,YAAY,CAAK,CACjC,EAEA,MAAO,OAAO,eAAgB,CAC5B,MAAM,EAAM,QAAQ,CACtB,CACF,EAEA,MAAO,CAAE,aAAgB,EAAU,eAAgB,QAAO,UAAS,CACrE,CAGA,SAAgB,gBACd,EACA,EACA,EAAgC,CAAE,QAAO,EAClB,CACvB,OAAO,eAAe,EAAQ,EAAM,CAAO,CAAC,CAAC,KAC/C,CAGA,SAAgB,mBACd,EACA,EACA,EACuB,CACvB,GAAM,CAAE,WAAU,iBAAgB,QAAO,YAAa,eAAe,EAAQ,EAAM,CAAO,EACpF,EAAQ,EAAQ,YAAY,CAAE,iBAAgB,UAAS,CAAC,EAExD,UACJ,EACA,IAEI,EAAS,EAAU,QAAQ,OAAO,IAAI,EAAA,kBAAoB,EAEvD,EAAM,EAAQ,CAAE,EAKzB,OAAO,OAAO,OAAO,EAAO,CAC1B,MAAO,SACP,WAAuC,EAAU,EAA+C,CAC9F,OAAO,SAAS,CAAC,CAAK,EAAI,GAAO,EAAG,WAAW,EAAO,CAAI,CAAC,CAC7D,EACA,OACE,EACA,EACA,EACA,EACqC,CACrC,OAAO,SAAS,CAAC,CAAK,EAAI,GAAO,EAAG,OAAO,EAAO,EAAK,EAAS,CAAG,CAAC,CACtE,EACA,OACE,EACA,EACA,EACA,EACyB,CACzB,OAAO,SAAS,CAAC,CAAK,EAAI,GAAO,EAAG,OAAO,EAAO,EAAK,EAAO,CAAG,CAAC,CACpE,CACF,CAAC,CACH"}
@@ -1,37 +1,8 @@
1
- import type { AnySchema, DocumentVaultStore, DurableStoreOptions, KeyOf, KeyValueVaultStore, MemoryStoreOptions, RecordOf, TableCodecs, TransactionContext } from './types';
1
+ import type { AnySchema, DocumentVaultStore, KeyValueVaultStore, RecordOf, TableCodecs } from './types';
2
2
  export declare function assertBatchTables(tables: readonly string[]): void;
3
3
  export declare function makeValidator<S extends AnySchema>(codecs: TableCodecs<S> | undefined): <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;
4
- export declare function buildTxContext<S extends AnySchema, K extends keyof S & string>(schema: S, core: StorageBackend<S, K>, onMutate: (table: K) => void, validate?: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>, scope?: ReadonlySet<string>, isActive?: () => boolean): TransactionContext<S, K>;
5
- /** Builds a KeyValueVaultStore from a backend and options. */
6
- export declare function buildKeyValueStore<S extends AnySchema>(schema: S, core: StorageBackend<S>, options?: MemoryStoreOptions<S> & {
7
- onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;
8
- onMutation?: (table: keyof S & string) => void;
9
- onTransactions?: (deps: BatchDeps<S>) => void;
10
- }): KeyValueVaultStore<S>;
11
- /** A document store before batch() is attached. */
12
- export type DocumentVaultStoreWithoutBatch<S extends AnySchema> = Omit<DocumentVaultStore<S>, 'batch'>;
13
- /** Builds a DocumentVaultStore (without batch) from a backend, codecs, and options. */
14
- export declare function buildDocumentStore<S extends AnySchema>(schema: S, core: StorageBackend<S>, options: DurableStoreOptions<S> & {
15
- onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;
16
- onMutation?: (table: keyof S & string) => void;
17
- onTransactions?: (deps: BatchDeps<S>) => void;
18
- }): DocumentVaultStoreWithoutBatch<S>;
19
- /** Attaches a batch() implementation to a document store. */
20
- export declare function withBatch<S extends AnySchema>(store: DocumentVaultStoreWithoutBatch<S>, batch: BatchImpl<S>, schema: S): DocumentVaultStore<S>;
21
- /** Check whether a record exists for the given key. */
22
- export declare function has<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K, key: KeyOf<S, K>): Promise<boolean>;
23
- /** Count live records in a table. */
24
- export declare function count<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K): Promise<number>;
25
- /** Check whether a table has no live records. */
26
- export declare function isEmpty<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K): Promise<boolean>;
27
- /** Fetch multiple records by key. Preserves key order; missing keys yield `undefined`. */
28
- export declare function getMany<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K, keys: readonly KeyOf<S, K>[]): Promise<Array<RecordOf<S, K> | undefined>>;
29
- /** Fetch all live primary keys for a table. */
30
- export declare function keys<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K, filter?: (record: RecordOf<S, K>) => boolean): Promise<KeyOf<S, K>[]>;
31
- /** Delete multiple records by key. Returns the count of records that were actually deleted. */
32
- export declare function deleteMany<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K, keys: readonly KeyOf<S, K>[]): Promise<number>;
33
- /** Partially update an existing record. Returns `undefined` when the key does not exist. */
34
- export declare function update<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K, key: KeyOf<S, K>, changes: Partial<RecordOf<S, K>>, ttl?: number): Promise<RecordOf<S, K> | undefined>;
35
- /** Read-modify-write: the callback receives the current record (or `undefined`) and returns the value to store. */
36
- export declare function upsert<S extends AnySchema, K extends keyof S & string>(store: KeyValueVaultStore<S>, table: K, key: KeyOf<S, K>, apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>, ttl?: number): Promise<RecordOf<S, K>>;
4
+ /** Builds a key-value store: core CRUD, observation, TTL, and pruning. */
5
+ export declare function buildVaultStore<S extends AnySchema>(schema: S, core: StorageBackend<S>, options?: VaultStoreOptions<S>): KeyValueVaultStore<S>;
6
+ /** Builds a document store: the key-value surface plus atomic `batch()`. */
7
+ export declare function buildDocumentStore<S extends AnySchema>(schema: S, core: StorageBackend<S>, options: DocumentStoreOptions<S>): DocumentVaultStore<S>;
37
8
  //# sourceMappingURL=adapter-core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.d.ts","sourceRoot":"","sources":["../src/adapter-core.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter-core.d.ts","sourceRoot":"","sources":["../src/adapter-core.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAElB,kBAAkB,EAElB,QAAQ,EACR,WAAW,EAEZ,MAAM,SAAS,CAAC;AA0CjB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEjE;AA8CD,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAC/C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,GACjC,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAYjF;AAsPD,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,EACjD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,OAAO,GAAE,iBAAiB,CAAC,CAAC,CAAc,GACzC,kBAAkB,CAAC,CAAC,CAAC,CAEvB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EACpD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,kBAAkB,CAAC,CAAC,CAAC,CAqCvB"}
@@ -1,283 +1,2 @@
1
- import { validateRecord as e } from "./codec.js";
2
- import { VaultDisposedError as t, VaultError as n, VaultScopeError as r } from "./errors.js";
3
- import { createObserverHub as i, getRecordKey as a } from "./internal.js";
4
- import { createQueryBuilder as o } from "./query.js";
5
- import { assertPositiveFinite as s } from "./ttl.js";
6
- //#region src/adapter-core.ts
7
- function c(e) {
8
- if (e.length === 0) throw new n("batch: declare at least one table");
9
- }
10
- function l(e, t, n) {
11
- return n !== void 0 && s(n, "put/putAll"), n ?? e[t].defaultTtl;
12
- }
13
- function u(e, t, r, i, o) {
14
- let s = a(e, t, i);
15
- if (s !== r) throw new n(`${o}: key field "${e[t].key}" must be "${String(r)}" but got "${String(s)}" in table "${t}"`);
16
- }
17
- function d(t) {
18
- return (r, i) => {
19
- let a = t?.[r];
20
- if (!a) return i;
21
- try {
22
- return e(a, i);
23
- } catch (e) {
24
- throw new n(`validation failed for table "${r}"`, { cause: e });
25
- }
26
- };
27
- }
28
- function f(e, t, n, i, s, c = () => !0) {
29
- let d = i ?? ((e, t) => t), f = (e) => {
30
- if (!c()) throw new r("transaction context is no longer active");
31
- if (s && !s.has(e)) throw new r(`table "${e}" is not part of this batch scope`);
32
- }, p = {
33
- async clear(e) {
34
- f(e), await t.clear(e), n(e);
35
- },
36
- async count(e) {
37
- return f(e), t.count ? t.count(e) : (await t.getAll(e)).length;
38
- },
39
- async delete(e, r) {
40
- f(e);
41
- let i = await t.delete(e, r);
42
- return i && n(e), i;
43
- },
44
- async deleteMany(e, r) {
45
- if (f(e), !t.deleteMany) {
46
- let i = (await Promise.all(r.map((n) => t.delete(e, n)))).filter(Boolean).length;
47
- return i > 0 && n(e), i;
48
- }
49
- let i = await t.deleteMany(e, r);
50
- return i > 0 && n(e), i;
51
- },
52
- async get(e, n) {
53
- return f(e), t.get(e, n);
54
- },
55
- async getAll(e) {
56
- return f(e), t.getAll(e);
57
- },
58
- async getMany(e, n) {
59
- return f(e), t.getMany ? t.getMany(e, n) : Promise.all(n.map((n) => t.get(e, n)));
60
- },
61
- async has(e, n) {
62
- return f(e), t.has ? t.has(e, n) : await t.get(e, n) !== void 0;
63
- },
64
- async isEmpty(e) {
65
- return await p.count(e) === 0;
66
- },
67
- iterate(e) {
68
- return f(e), t.iterate ? t.iterate(e) : (async function* () {
69
- let n = await t.getAll(e);
70
- for (let e of n) yield e;
71
- })();
72
- },
73
- async keys(n, r) {
74
- if (f(n), !r && t.getAllKeys) return t.getAllKeys(n);
75
- let i = await t.getAll(n), a = r ? i.filter(r) : i, o = e[n].key;
76
- return a.map((e) => e[o]);
77
- },
78
- async put(r, i, a) {
79
- f(r);
80
- let o = d(r, i);
81
- return await t.put(r, o, l(e, r, a)), n(r), o;
82
- },
83
- async putAll(r, i, a) {
84
- if (f(r), i.length === 0) return;
85
- let o = i.map((e) => d(r, e));
86
- await t.putAll(r, o, l(e, r, a)), n(r);
87
- },
88
- query(t) {
89
- return f(t), o({
90
- deleteMany: async (n) => {
91
- let r = n.map((n) => a(e, t, n));
92
- return p.deleteMany(t, r);
93
- },
94
- source: () => p.getAll(t)
95
- });
96
- },
97
- async update(t, n, r, i) {
98
- let a = await p.get(t, n);
99
- if (a === void 0) return;
100
- let o = {
101
- ...a,
102
- ...r
103
- };
104
- return u(e, t, n, o, "update"), p.put(t, o, i);
105
- },
106
- async upsert(t, n, r, i) {
107
- let a = r(await p.get(t, n));
108
- return u(e, t, n, a, "upsert: update()"), p.put(t, a, i);
109
- }
110
- };
111
- return p;
112
- }
113
- function p(e, n, r) {
114
- let { codecs: s } = r ?? {}, c = i((e) => n.getAll(e)), f = (e) => {
115
- c.notify(e), r?.onMutation?.(e);
116
- }, p = (e) => {
117
- c.notify(e);
118
- }, m = n.pruneAllExpired?.bind(n), h = r?.onCrossTabMessage?.(p) ?? void 0, g = d(s);
119
- r?.onTransactions?.({
120
- notifyMutation: f,
121
- validate: g
122
- });
123
- let _ = new AbortController(), v = !1, y = () => {
124
- if (v) throw new t();
125
- }, b = {
126
- async clear(e) {
127
- y(), await n.clear(e), f(e);
128
- },
129
- async count(e) {
130
- return y(), n.count ? n.count(e) : (await n.getAll(e)).length;
131
- },
132
- async delete(e, t) {
133
- y();
134
- let r = await n.delete(e, t);
135
- return r && f(e), r;
136
- },
137
- async deleteMany(e, t) {
138
- y();
139
- let r = n.deleteMany ? await n.deleteMany(e, t) : (await Promise.all(t.map((t) => n.delete(e, t)))).filter(Boolean).length;
140
- return r > 0 && f(e), r;
141
- },
142
- get disposalSignal() {
143
- return _.signal;
144
- },
145
- async dispose() {
146
- v || (v = !0, _.abort(), h?.(), c.dispose(), await n.dispose?.());
147
- },
148
- get disposed() {
149
- return v;
150
- },
151
- async get(e, t) {
152
- return y(), n.get(e, t);
153
- },
154
- async getAll(e) {
155
- return y(), n.getAll(e);
156
- },
157
- async getMany(e, t) {
158
- return y(), n.getMany ? n.getMany(e, t) : Promise.all(t.map((t) => n.get(e, t)));
159
- },
160
- async has(e, t) {
161
- return y(), n.has ? n.has(e, t) : await n.get(e, t) !== void 0;
162
- },
163
- async isEmpty(e) {
164
- return await b.count(e) === 0;
165
- },
166
- async keys(t, r) {
167
- if (y(), !r && n.getAllKeys) return n.getAllKeys(t);
168
- let i = await n.getAll(t), a = r ? i.filter(r) : i, o = e[t].key;
169
- return a.map((e) => e[o]);
170
- },
171
- observe(e, t, n) {
172
- return y(), c.observe(e, t, n);
173
- },
174
- async pruneExpired() {
175
- if (y(), m) {
176
- let e = await m();
177
- for (let [t, n] of Object.entries(e)) n > 0 && f(t);
178
- return e;
179
- }
180
- let t = Object.keys(e), r = await Promise.all(t.map(async (e) => [e, await n.pruneExpiredInTable(e)]));
181
- for (let [e, t] of r) t > 0 && f(e);
182
- return Object.fromEntries(r);
183
- },
184
- async put(t, r, i) {
185
- y();
186
- let a = g(t, r);
187
- return await n.put(t, a, l(e, t, i)), f(t), a;
188
- },
189
- async putAll(t, r, i) {
190
- if (y(), r.length === 0) return;
191
- let a = r.map((e) => g(t, e));
192
- await n.putAll(t, a, l(e, t, i)), f(t);
193
- },
194
- query(t) {
195
- return y(), o({
196
- deleteMany: async (n) => {
197
- let r = n.map((n) => a(e, t, n));
198
- return b.deleteMany(t, r);
199
- },
200
- source: () => b.getAll(t)
201
- });
202
- },
203
- async update(t, n, r, i) {
204
- let a = await b.get(t, n);
205
- if (a === void 0) return;
206
- let o = {
207
- ...a,
208
- ...r
209
- };
210
- return u(e, t, n, o, "update"), b.put(t, o, i);
211
- },
212
- async upsert(t, n, r, i) {
213
- let a = r(await b.get(t, n));
214
- return u(e, t, n, a, "upsert: apply()"), b.put(t, a, i);
215
- },
216
- async [Symbol.asyncDispose]() {
217
- await b.dispose();
218
- }
219
- };
220
- return b;
221
- }
222
- function m(e, n, r) {
223
- let i = p(e, n, r);
224
- return Object.assign(i, { iterate(e) {
225
- if (i.disposed) throw new t();
226
- return n.iterate ? n.iterate(e) : (async function* () {
227
- for (let t of await i.getAll(e)) yield t;
228
- })();
229
- } });
230
- }
231
- function h(e, n, r) {
232
- let i = (r, i) => e.disposed ? Promise.reject(new t()) : n(r, i);
233
- return Object.assign(e, {
234
- batch: i,
235
- deleteMany(e, t) {
236
- return i([e], (n) => n.deleteMany(e, t));
237
- },
238
- query(n) {
239
- if (e.disposed) throw new t();
240
- return o({
241
- deleteMany: async (e) => {
242
- let t = e.map((e) => a(r, n, e));
243
- return i([n], (e) => e.deleteMany(n, t));
244
- },
245
- source: () => e.getAll(n)
246
- });
247
- },
248
- update(e, t, n, r) {
249
- return i([e], (i) => i.update(e, t, n, r));
250
- },
251
- upsert(e, t, n, r) {
252
- return i([e], (i) => i.upsert(e, t, n, r));
253
- }
254
- });
255
- }
256
- async function g(e, t, n) {
257
- return e.has(t, n);
258
- }
259
- async function _(e, t) {
260
- return e.count(t);
261
- }
262
- async function v(e, t) {
263
- return e.isEmpty(t);
264
- }
265
- async function y(e, t, n) {
266
- return e.getMany(t, n);
267
- }
268
- async function b(e, t, n) {
269
- return e.keys(t, n);
270
- }
271
- async function x(e, t, n) {
272
- return e.deleteMany(t, n);
273
- }
274
- async function S(e, t, n, r, i) {
275
- return e.update(t, n, r, i);
276
- }
277
- async function C(e, t, n, r, i) {
278
- return e.upsert(t, n, r, i);
279
- }
280
- //#endregion
281
- export { c as assertBatchTables, m as buildDocumentStore, p as buildKeyValueStore, f as buildTxContext, _ as count, x as deleteMany, y as getMany, g as has, v as isEmpty, b as keys, d as makeValidator, S as update, C as upsert, h as withBatch };
282
-
1
+ import{VaultDisposedError as e,VaultError as t,VaultScopeError as n}from"./errors.js";import{assertPositiveFinite as r}from"./ttl.js";import{validateRecord as i}from"./codec.js";import{createObserverHub as a,getRecordKey as o}from"./internal.js";function assertBatchTables(e){if(e.length===0)throw new t(`batch: declare at least one table`)}function batchScopeGuard(e,t=()=>!0){let r=new Set(e);return e=>{if(!t())throw new n(`transaction context is no longer active`);if(!r.has(e))throw new n(`table "${e}" is not part of this batch scope`)}}function resolveTtl(e,t,n){return n!==void 0&&r(n,`put/putAll`),n??e[t].defaultTtl}function verifyKey(e,n,r,i,a){let s=o(e,n,i);if(s!==r)throw new t(`${a}: key field "${e[n].key}" must be "${String(r)}" but got "${String(s)}" in table "${n}"`)}function makeValidator(e){return(n,r)=>{let a=e?.[n];if(!a)return r;try{return i(a,r)}catch(e){throw new t(`validation failed for table "${n}"`,{cause:e})}}}function buildOperations(e,t,n,r,i){let a={async clear(e){i(e),await t.clear(e),n(e)},async count(e){return i(e),(await t.getAll(e)).length},async delete(e,r){i(e);let a=await t.delete(e,r);return a&&n(e),a},async deleteMany(e,r){i(e);let a=t.deleteMany?await t.deleteMany(e,r):(await Promise.all(r.map(n=>t.delete(e,n)))).filter(Boolean).length;return a>0&&n(e),a},async get(e,n){return i(e),t.get(e,n)},async getAll(e){return i(e),t.getAll(e)},async getMany(e,n){return i(e),t.getMany?t.getMany(e,n):Promise.all(n.map(n=>t.get(e,n)))},async has(e,n){return i(e),await t.get(e,n)!==void 0},async isEmpty(e){return await a.count(e)===0},iterate(e){return i(e),t.iterate?t.iterate(e):(async function*(){for(let n of await t.getAll(e))yield n})()},async keys(n,r){i(n);let a=await t.getAll(n),o=r?a.filter(r):a,s=e[n].key;return o.map(e=>e[s])},async put(a,o,s){i(a);let c=r(a,o);return await t.put(a,c,resolveTtl(e,a,s)),n(a),c},async putAll(a,o,s){if(i(a),o.length===0)return;let c=o.map(e=>r(a,e));await t.putAll(a,c,resolveTtl(e,a,s)),n(a)},async update(t,n,r,i){let o=await a.get(t,n);if(o===void 0)return;let s={...o,...r};return verifyKey(e,t,n,s,`update`),a.put(t,s,i)},async upsert(t,n,r,i){let o=r(await a.get(t,n));return verifyKey(e,t,n,o,`upsert: apply()`),a.put(t,o,i)}};return a}function buildStoreCore(t,n,r){let i=a(e=>n.getAll(e)),notifyMutation=e=>{i.notify(e),r.onMutation?.(e)},notifyExternal=e=>{i.notify(e)},o=r.onCrossTabMessage?.(notifyExternal)??void 0,s=makeValidator(r.codecs),c=n.pruneAllExpired?.bind(n),l=new AbortController,u=!1,checkDisposed=()=>{if(u)throw new e},d={...buildOperations(t,n,notifyMutation,s,()=>checkDisposed()),get disposalSignal(){return l.signal},async dispose(){u||(u=!0,l.abort(),o?.(),i.dispose(),await n.dispose?.())},get disposed(){return u},observe(e,t,n){return checkDisposed(),i.observe(e,t,n)},async pruneExpired(){if(checkDisposed(),c){let e=await c();for(let[t,n]of Object.entries(e))n>0&&notifyMutation(t);return e}let e=Object.keys(t),r=await Promise.all(e.map(async e=>[e,await n.pruneExpiredInTable(e)]));for(let[e,t]of r)t>0&&notifyMutation(e);return Object.fromEntries(r)},async[Symbol.asyncDispose](){await d.dispose()}};return{disposed:()=>u,notifyMutation,store:d,validate:s}}function buildVaultStore(e,t,n={schema:e}){return buildStoreCore(e,t,n).store}function buildDocumentStore(t,n,r){let{disposed:i,notifyMutation:a,store:o,validate:s}=buildStoreCore(t,n,r),c=r.createBatch({notifyMutation:a,validate:s}),runBatch=(t,n)=>i()?Promise.reject(new e):c(t,n);return Object.assign(o,{batch:runBatch,deleteMany(e,t){return runBatch([e],n=>n.deleteMany(e,t))},update(e,t,n,r){return runBatch([e],i=>i.update(e,t,n,r))},upsert(e,t,n,r){return runBatch([e],i=>i.upsert(e,t,n,r))}})}export{assertBatchTables,batchScopeGuard,buildDocumentStore,buildOperations,buildVaultStore,makeValidator};
283
2
  //# sourceMappingURL=adapter-core.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.js","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { validateRecord } from './codec';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder } from './query';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n DocumentVaultStore,\n DurableStoreOptions,\n KeyOf,\n KeyValueVaultStore,\n MemoryStoreOptions,\n RecordOf,\n TableCodecs,\n TransactionContext,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/** @internal Full backend protocol implemented by each adapter. */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n count?<T extends K>(table: T): Promise<number>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n deleteMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n dispose?(): Promise<void>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n getAllKeys?<T extends K>(table: T): Promise<KeyOf<S, T>[]>;\n getMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n has?<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n /**\n * Optional: lazy iteration over live records. Document stores implement this natively\n * (IDB cursor, SQLite keyset pagination). Key-value stores fall back to getAll().\n */\n iterate?<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n /**\n * Optional: prune all expired records across all tables in a single atomic operation.\n * When present, `pruneExpired()` delegates here instead of calling `pruneExpiredInTable` N times.\n */\n pruneAllExpired?(): Promise<Record<string, number>>;\n pruneExpiredInTable<T extends K>(table: T): Promise<number>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** @internal */\nexport type BatchDeps<S extends AnySchema> = {\n notifyMutation: (table: keyof S & string) => void;\n validate: <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;\n};\n\n/** @internal */\nexport type BatchImpl<S extends AnySchema> = <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n) => Promise<R>;\n\n/* -------------------- Helpers -------------------- */\n\nexport function assertBatchTables(tables: readonly string[]): void {\n if (tables.length === 0) throw new VaultError('batch: declare at least one table');\n}\n\nfunction resolveTtl<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n ttl?: number,\n): number | undefined {\n if (ttl !== undefined) assertPositiveFinite(ttl, 'put/putAll');\n\n return ttl ?? schema[table].defaultTtl;\n}\n\nfunction verifyKey<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n expected: KeyOf<S, K>,\n value: RecordOf<S, K>,\n op: string,\n): void {\n const actual = getRecordKey(schema, table, value);\n\n if (actual !== expected) {\n throw new VaultError(\n `${op}: key field \"${schema[table].key}\" must be \"${String(expected)}\" but got \"${String(actual)}\" in table \"${table}\"`,\n );\n }\n}\n\n/* -------------------- Codec helpers -------------------- */\n\nexport function makeValidator<S extends AnySchema>(\n codecs: TableCodecs<S> | undefined,\n): <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K> {\n return <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const codec = codecs?.[table];\n\n if (!codec) return value;\n\n try {\n return validateRecord(codec, value);\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n}\n\n/* -------------------- buildTxContext -------------------- */\n\nexport function buildTxContext<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n core: StorageBackend<S, K>,\n onMutate: (table: K) => void,\n validate?: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n scope?: ReadonlySet<string>,\n isActive: () => boolean = () => true,\n): TransactionContext<S, K> {\n const applyValidation = validate ?? ((_: K, v: RecordOf<S, K>) => v);\n\n const checkScope = (table: K): void => {\n if (!isActive()) throw new VaultScopeError('transaction context is no longer active');\n if (scope && !scope.has(table)) throw new VaultScopeError(`table \"${table}\" is not part of this batch scope`);\n };\n\n const context: TransactionContext<S, K> = {\n async clear(table) {\n checkScope(table);\n await core.clear(table);\n onMutate(table);\n },\n async count(table) {\n checkScope(table);\n return core.count ? core.count(table) : (await core.getAll(table)).length;\n },\n async delete(table, key) {\n checkScope(table);\n\n const deleted = await core.delete(table, key);\n\n if (deleted) onMutate(table);\n\n return deleted;\n },\n async deleteMany(table, keys) {\n checkScope(table);\n if (!core.deleteMany) {\n const results = await Promise.all(keys.map((key) => core.delete(table, key)));\n const deleted = results.filter(Boolean).length;\n if (deleted > 0) onMutate(table);\n return deleted;\n }\n const deleted = await core.deleteMany(table, keys);\n if (deleted > 0) onMutate(table);\n return deleted;\n },\n async get(table, key) {\n checkScope(table);\n\n return core.get(table, key);\n },\n async getAll(table) {\n checkScope(table);\n\n return core.getAll(table);\n },\n async getMany(table, keys) {\n checkScope(table);\n return core.getMany ? core.getMany(table, keys) : Promise.all(keys.map((key) => core.get(table, key)));\n },\n async has(table, key) {\n checkScope(table);\n return core.has ? core.has(table, key) : (await core.get(table, key)) !== undefined;\n },\n async isEmpty(table) {\n return (await context.count(table)) === 0;\n },\n iterate(table) {\n checkScope(table);\n\n if (core.iterate) return core.iterate(table);\n\n // Fallback: iterate over getAll() materialized records.\n return (async function* (): AsyncIterable<RecordOf<S, typeof table>> {\n const records = await core.getAll(table);\n\n for (const record of records) yield record;\n })();\n },\n async keys(table, filter) {\n checkScope(table);\n if (!filter && core.getAllKeys) return core.getAllKeys(table);\n const records = await core.getAll(table);\n const selected = filter ? records.filter(filter) : records;\n const keyField = schema[table].key;\n return selected.map((record) => (record as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n checkScope(table);\n const canonical = applyValidation(table, value);\n await core.put(table, canonical, resolveTtl(schema, table, ttl));\n onMutate(table);\n return canonical;\n },\n async putAll(table, values, ttl) {\n checkScope(table);\n\n if (values.length === 0) return;\n\n const toWrite = values.map((v) => applyValidation(table, v));\n\n await core.putAll(table, toWrite, resolveTtl(schema, table, ttl));\n onMutate(table);\n },\n query(table) {\n checkScope(table);\n return createQueryBuilder({\n deleteMany: async (records) => {\n const keys = records.map((record) => getRecordKey(schema, table, record));\n return context.deleteMany(table, keys);\n },\n source: () => context.getAll(table),\n });\n },\n async update(table, key, changes, ttl) {\n const current = await context.get(table, key);\n if (current === undefined) return undefined;\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n verifyKey(schema, table, key, merged, 'update');\n return context.put(table, merged, ttl);\n },\n async upsert(table, key, update, ttl) {\n const value = update(await context.get(table, key));\n verifyKey(schema, table, key, value, 'upsert: update()');\n return context.put(table, value, ttl);\n },\n };\n\n return context;\n}\n\n/* -------------------- buildKeyValueStore -------------------- */\n\n/** Builds a KeyValueVaultStore from a backend and options. */\nexport function buildKeyValueStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: MemoryStoreOptions<S> & {\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n onTransactions?: (deps: BatchDeps<S>) => void;\n },\n): KeyValueVaultStore<S> {\n const { codecs } = options ?? {};\n\n const observers = createObserverHub<S>((table) => core.getAll(table));\n\n const notifyMutation = (table: keyof S & string): void => {\n observers.notify(table);\n options?.onMutation?.(table);\n };\n\n // Cross-tab notifications must NOT call onMutation (which would re-publish to BroadcastChannel,\n // creating an infinite loop). They only notify local observers.\n const notifyExternal = (table: keyof S & string): void => {\n observers.notify(table);\n };\n\n const pruneAll = core.pruneAllExpired?.bind(core);\n\n const disconnectExternal = options?.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = makeValidator(codecs);\n\n options?.onTransactions?.({ notifyMutation, validate });\n\n const disposeController = new AbortController();\n let disposed = false;\n\n const checkDisposed = (): void => {\n if (disposed) throw new VaultDisposedError();\n };\n\n const adapter: KeyValueVaultStore<S> = {\n async clear(table) {\n checkDisposed();\n await core.clear(table);\n notifyMutation(table);\n },\n async count(table) {\n checkDisposed();\n return core.count ? core.count(table) : (await core.getAll(table)).length;\n },\n\n async delete(table, key) {\n checkDisposed();\n\n const deleted = await core.delete(table, key);\n\n if (deleted) notifyMutation(table);\n\n return deleted;\n },\n async deleteMany(table, keys) {\n checkDisposed();\n const deleted = core.deleteMany\n ? await core.deleteMany(table, keys)\n : (await Promise.all(keys.map((key) => core.delete(table, key)))).filter(Boolean).length;\n if (deleted > 0) notifyMutation(table);\n return deleted;\n },\n\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n async dispose() {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n disconnectExternal?.();\n observers.dispose();\n await core.dispose?.();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n async get(table, key) {\n checkDisposed();\n\n return core.get(table, key);\n },\n\n async getAll(table) {\n checkDisposed();\n\n return core.getAll(table);\n },\n async getMany(table, keys) {\n checkDisposed();\n return core.getMany ? core.getMany(table, keys) : Promise.all(keys.map((key) => core.get(table, key)));\n },\n async has(table, key) {\n checkDisposed();\n return core.has ? core.has(table, key) : (await core.get(table, key)) !== undefined;\n },\n async isEmpty(table) {\n return (await adapter.count(table)) === 0;\n },\n async keys(table, filter) {\n checkDisposed();\n if (!filter && core.getAllKeys) return core.getAllKeys(table);\n const records = await core.getAll(table);\n const selected = filter ? records.filter(filter) : records;\n const keyField = schema[table].key;\n return selected.map((record) => (record as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n\n observe(table, listener, opts) {\n checkDisposed();\n\n return observers.observe(table, listener, opts);\n },\n\n async pruneExpired() {\n checkDisposed();\n\n if (pruneAll) {\n const result = await pruneAll();\n\n for (const [table, pruned] of Object.entries(result)) {\n if (pruned > 0) notifyMutation(table as keyof S & string);\n }\n\n return result as { [K in keyof S & string]: number };\n }\n\n const tableNames = Object.keys(schema) as Array<keyof S & string>;\n const pairs = await Promise.all(\n tableNames.map(async (name) => {\n const pruned = await core.pruneExpiredInTable(name);\n\n return [name, pruned] as const;\n }),\n );\n\n for (const [table, pruned] of pairs) {\n if (pruned > 0) notifyMutation(table);\n }\n\n return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async put(table, value, ttl) {\n checkDisposed();\n const canonical = validate(table, value);\n await core.put(table, canonical, resolveTtl(schema, table, ttl));\n notifyMutation(table);\n return canonical;\n },\n\n async putAll(table, values, ttl) {\n checkDisposed();\n\n if (values.length === 0) return;\n\n const toWrite = values.map((v) => validate(table, v));\n\n await core.putAll(table, toWrite, resolveTtl(schema, table, ttl));\n notifyMutation(table);\n },\n query(table) {\n checkDisposed();\n return createQueryBuilder({\n deleteMany: async (records) => {\n const keys = records.map((record) => getRecordKey(schema, table, record));\n return adapter.deleteMany(table, keys);\n },\n source: () => adapter.getAll(table),\n });\n },\n async update(table, key, changes, ttl) {\n const current = await adapter.get(table, key);\n if (current === undefined) return undefined;\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n verifyKey(schema, table, key, merged, 'update');\n return adapter.put(table, merged, ttl);\n },\n async upsert(table, key, apply, ttl) {\n const value = apply(await adapter.get(table, key));\n verifyKey(schema, table, key, value, 'upsert: apply()');\n return adapter.put(table, value, ttl);\n },\n\n async [Symbol.asyncDispose]() {\n await adapter.dispose();\n },\n };\n\n return adapter;\n}\n\n/* -------------------- buildDocumentStore -------------------- */\n\n/** A document store before batch() is attached. */\nexport type DocumentVaultStoreWithoutBatch<S extends AnySchema> = Omit<DocumentVaultStore<S>, 'batch'>;\n\n/** Builds a DocumentVaultStore (without batch) from a backend, codecs, and options. */\nexport function buildDocumentStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: DurableStoreOptions<S> & {\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n onTransactions?: (deps: BatchDeps<S>) => void;\n },\n): DocumentVaultStoreWithoutBatch<S> {\n const store = buildKeyValueStore(schema, core, options);\n\n return Object.assign(store, {\n iterate<K extends keyof S & string>(table: K): AsyncIterable<RecordOf<S, K>> {\n if (store.disposed) throw new VaultDisposedError();\n if (core.iterate) return core.iterate(table);\n return (async function* (): AsyncIterable<RecordOf<S, K>> {\n for (const record of await store.getAll(table)) yield record;\n })();\n },\n });\n}\n\n/** Attaches a batch() implementation to a document store. */\nexport function withBatch<S extends AnySchema>(\n store: DocumentVaultStoreWithoutBatch<S>,\n batch: BatchImpl<S>,\n schema: S,\n): DocumentVaultStore<S> {\n const runBatch: BatchImpl<S> = (tables, fn) => {\n if (store.disposed) return Promise.reject(new VaultDisposedError());\n return batch(tables, fn);\n };\n\n return Object.assign(store, {\n batch: runBatch,\n deleteMany<K extends keyof S & string>(table: K, keys: readonly KeyOf<S, K>[]) {\n return runBatch([table], (tx) => tx.deleteMany(table, keys));\n },\n query<K extends keyof S & string>(table: K) {\n if (store.disposed) throw new VaultDisposedError();\n return createQueryBuilder({\n deleteMany: async (records) => {\n const keys = records.map((record) => getRecordKey(schema, table, record));\n return runBatch([table], (tx) => tx.deleteMany(table, keys));\n },\n source: () => store.getAll(table),\n });\n },\n update<K extends keyof S & string>(table: K, key: KeyOf<S, K>, changes: Partial<RecordOf<S, K>>, ttl?: number) {\n return runBatch([table], (tx) => tx.update(table, key, changes, ttl));\n },\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ) {\n return runBatch([table], (tx) => tx.upsert(table, key, apply, ttl));\n },\n }) as DocumentVaultStore<S>;\n}\n\n/* -------------------- Derived convenience helpers -------------------- */\n\n/** Check whether a record exists for the given key. */\nexport async function has<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n key: KeyOf<S, K>,\n): Promise<boolean> {\n return store.has(table, key);\n}\n\n/** Count live records in a table. */\nexport async function count<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n): Promise<number> {\n return store.count(table);\n}\n\n/** Check whether a table has no live records. */\nexport async function isEmpty<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n): Promise<boolean> {\n return store.isEmpty(table);\n}\n\n/** Fetch multiple records by key. Preserves key order; missing keys yield `undefined`. */\nexport async function getMany<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n keys: readonly KeyOf<S, K>[],\n): Promise<Array<RecordOf<S, K> | undefined>> {\n return store.getMany(table, keys);\n}\n\n/** Fetch all live primary keys for a table. */\nexport async function keys<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n filter?: (record: RecordOf<S, K>) => boolean,\n): Promise<KeyOf<S, K>[]> {\n return store.keys(table, filter);\n}\n\n/** Delete multiple records by key. Returns the count of records that were actually deleted. */\nexport async function deleteMany<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n keys: readonly KeyOf<S, K>[],\n): Promise<number> {\n return store.deleteMany(table, keys);\n}\n\n/** Partially update an existing record. Returns `undefined` when the key does not exist. */\nexport async function update<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n): Promise<RecordOf<S, K> | undefined> {\n return store.update(table, key, changes, ttl);\n}\n\n/** Read-modify-write: the callback receives the current record (or `undefined`) and returns the value to store. */\nexport async function upsert<S extends AnySchema, K extends keyof S & string>(\n store: KeyValueVaultStore<S>,\n table: K,\n key: KeyOf<S, K>,\n apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n): Promise<RecordOf<S, K>> {\n return store.upsert(table, key, apply, ttl);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"adapter-core.js","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { validateRecord } from './codec';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n DocumentVaultStore,\n KeyOf,\n KeyValueVaultStore,\n MemoryStoreOptions,\n RecordOf,\n TableCodecs,\n TransactionContext,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/**\n * @internal Backend protocol implemented by each adapter. Only operations an\n * adapter can perform natively appear here; every convenience is layered on\n * once in `buildOperations`.\n */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n delete<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\n /** Native multi-key delete in one transaction (IndexedDB). Falls back to per-key deletes. */\n deleteMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<number>;\n dispose?(): Promise<void>;\n get<T extends K>(table: T, key: KeyOf<S, T>): Promise<RecordOf<S, T> | undefined>;\n getAll<T extends K>(table: T): Promise<RecordOf<S, T>[]>;\n /** Native multi-key read in one transaction (IndexedDB). Falls back to per-key reads. */\n getMany?<T extends K>(table: T, keys: readonly KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n /** Lazy iteration over live records. Adapters without native cursors fall back to `getAll()`. */\n iterate?<T extends K>(table: T): AsyncIterable<RecordOf<S, T>>;\n /** Prune expired records across all tables in a single operation. */\n pruneAllExpired?(): Promise<Record<string, number>>;\n pruneExpiredInTable<T extends K>(table: T): Promise<number>;\n put<T extends K>(table: T, value: RecordOf<S, T>, ttl?: number): Promise<void>;\n putAll<T extends K>(table: T, values: readonly RecordOf<S, T>[], ttl?: number): Promise<void>;\n};\n\n/** @internal Capabilities a batch factory needs from the surrounding store. */\nexport type BatchDeps<S extends AnySchema> = {\n notifyMutation: (table: keyof S & string) => void;\n validate: <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;\n};\n\n/** @internal */\nexport type BatchImpl<S extends AnySchema> = <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n) => Promise<R>;\n\n/* -------------------- Helpers -------------------- */\n\nexport function assertBatchTables(tables: readonly string[]): void {\n if (tables.length === 0) throw new VaultError('batch: declare at least one table');\n}\n\n/**\n * @internal Guard for transaction contexts: rejects use after the batch settled\n * and access to tables outside the declared scope.\n */\nexport function batchScopeGuard(\n tables: readonly string[],\n isActive: () => boolean = () => true,\n): (table: string) => void {\n const scope = new Set(tables);\n\n return (table): void => {\n if (!isActive()) throw new VaultScopeError('transaction context is no longer active');\n if (!scope.has(table)) throw new VaultScopeError(`table \"${table}\" is not part of this batch scope`);\n };\n}\n\nfunction resolveTtl<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n ttl?: number,\n): number | undefined {\n if (ttl !== undefined) assertPositiveFinite(ttl, 'put/putAll');\n\n return ttl ?? schema[table].defaultTtl;\n}\n\nfunction verifyKey<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n expected: KeyOf<S, K>,\n value: RecordOf<S, K>,\n op: string,\n): void {\n const actual = getRecordKey(schema, table, value);\n\n if (actual !== expected) {\n throw new VaultError(\n `${op}: key field \"${schema[table].key}\" must be \"${String(expected)}\" but got \"${String(actual)}\" in table \"${table}\"`,\n );\n }\n}\n\n/* -------------------- Codec helpers -------------------- */\n\nexport function makeValidator<S extends AnySchema>(\n codecs: TableCodecs<S> | undefined,\n): <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K> {\n return <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const codec = codecs?.[table];\n\n if (!codec) return value;\n\n try {\n return validateRecord(codec, value);\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n}\n\n/* -------------------- buildOperations -------------------- */\n\n/**\n * @internal The single implementation of every store and transaction operation.\n * A public store and a batch transaction context differ only in who learns\n * about a mutation and which guard rejects illegal use, so both are built here.\n */\nexport function buildOperations<S extends AnySchema, K extends keyof S & string = keyof S & string>(\n schema: S,\n core: StorageBackend<S, K>,\n notify: (table: K) => void,\n validate: <T extends K>(table: T, value: RecordOf<S, T>) => RecordOf<S, T>,\n guard: (table: K) => void,\n): TransactionContext<S, K> {\n const ops: TransactionContext<S, K> = {\n async clear(table) {\n guard(table);\n await core.clear(table);\n notify(table);\n },\n async count(table) {\n guard(table);\n\n return (await core.getAll(table)).length;\n },\n async delete(table, key) {\n guard(table);\n\n const deleted = await core.delete(table, key);\n\n if (deleted) notify(table);\n\n return deleted;\n },\n async deleteMany(table, keys) {\n guard(table);\n\n const deleted = core.deleteMany\n ? await core.deleteMany(table, keys)\n : (await Promise.all(keys.map((key) => core.delete(table, key)))).filter(Boolean).length;\n\n if (deleted > 0) notify(table);\n\n return deleted;\n },\n async get(table, key) {\n guard(table);\n\n return core.get(table, key);\n },\n async getAll(table) {\n guard(table);\n\n return core.getAll(table);\n },\n async getMany(table, keys) {\n guard(table);\n\n return core.getMany ? core.getMany(table, keys) : Promise.all(keys.map((key) => core.get(table, key)));\n },\n async has(table, key) {\n guard(table);\n\n return (await core.get(table, key)) !== undefined;\n },\n async isEmpty(table) {\n return (await ops.count(table)) === 0;\n },\n iterate(table) {\n guard(table);\n\n if (core.iterate) return core.iterate(table);\n\n return (async function* (): AsyncIterable<RecordOf<S, typeof table>> {\n for (const record of await core.getAll(table)) yield record;\n })();\n },\n async keys(table, filter) {\n guard(table);\n\n const records = await core.getAll(table);\n const selected = filter ? records.filter(filter) : records;\n const keyField = schema[table].key;\n\n return selected.map((record) => (record as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n guard(table);\n\n const canonical = validate(table, value);\n\n await core.put(table, canonical, resolveTtl(schema, table, ttl));\n notify(table);\n\n return canonical;\n },\n async putAll(table, values, ttl) {\n guard(table);\n\n if (values.length === 0) return;\n\n const toWrite = values.map((v) => validate(table, v));\n\n await core.putAll(table, toWrite, resolveTtl(schema, table, ttl));\n notify(table);\n },\n async update(table, key, changes, ttl) {\n const current = await ops.get(table, key);\n\n if (current === undefined) return undefined;\n\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n\n verifyKey(schema, table, key, merged, 'update');\n\n return ops.put(table, merged, ttl);\n },\n async upsert(table, key, apply, ttl) {\n const value = apply(await ops.get(table, key));\n\n verifyKey(schema, table, key, value, 'upsert: apply()');\n\n return ops.put(table, value, ttl);\n },\n };\n\n return ops;\n}\n\n/* -------------------- buildVaultStore -------------------- */\n\n/** @internal Options shared by every store builder. */\nexport type VaultStoreOptions<S extends AnySchema> = MemoryStoreOptions<S> & {\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n};\n\n/** @internal Options for document stores, which supply their own batch factory. */\nexport type DocumentStoreOptions<S extends AnySchema> = VaultStoreOptions<S> & {\n createBatch: (deps: BatchDeps<S>) => BatchImpl<S>;\n};\n\ntype StoreCore<S extends AnySchema> = {\n disposed: () => boolean;\n notifyMutation: (table: keyof S & string) => void;\n store: KeyValueVaultStore<S>;\n validate: <K extends keyof S & string>(table: K, value: RecordOf<S, K>) => RecordOf<S, K>;\n};\n\nfunction buildStoreCore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: VaultStoreOptions<S>,\n): StoreCore<S> {\n const observers = createObserverHub<S>((table) => core.getAll(table));\n\n const notifyMutation = (table: keyof S & string): void => {\n observers.notify(table);\n options.onMutation?.(table);\n };\n\n // Cross-tab notifications must NOT call onMutation (which would re-publish to\n // BroadcastChannel and create an infinite loop). They only notify local observers.\n const notifyExternal = (table: keyof S & string): void => {\n observers.notify(table);\n };\n\n const disconnectExternal = options.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = makeValidator(options.codecs);\n const pruneAll = core.pruneAllExpired?.bind(core);\n\n const disposeController = new AbortController();\n let disposed = false;\n\n const checkDisposed = (): void => {\n if (disposed) throw new VaultDisposedError();\n };\n\n const ops = buildOperations<S>(schema, core, notifyMutation, validate, () => checkDisposed());\n\n const store: KeyValueVaultStore<S> = {\n ...ops,\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n\n async dispose() {\n if (disposed) return;\n\n disposed = true;\n disposeController.abort();\n disconnectExternal?.();\n observers.dispose();\n await core.dispose?.();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n observe(table, listener, opts) {\n checkDisposed();\n\n return observers.observe(table, listener, opts);\n },\n\n async pruneExpired() {\n checkDisposed();\n\n if (pruneAll) {\n const result = await pruneAll();\n\n for (const [table, pruned] of Object.entries(result)) {\n if (pruned > 0) notifyMutation(table as keyof S & string);\n }\n\n return result as { [K in keyof S & string]: number };\n }\n\n const tableNames = Object.keys(schema) as Array<keyof S & string>;\n const pairs = await Promise.all(\n tableNames.map(async (name) => {\n const pruned = await core.pruneExpiredInTable(name);\n\n return [name, pruned] as const;\n }),\n );\n\n for (const [table, pruned] of pairs) {\n if (pruned > 0) notifyMutation(table);\n }\n\n return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async [Symbol.asyncDispose]() {\n await store.dispose();\n },\n };\n\n return { disposed: () => disposed, notifyMutation, store, validate };\n}\n\n/** Builds a key-value store: core CRUD, observation, TTL, and pruning. */\nexport function buildVaultStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: VaultStoreOptions<S> = { schema },\n): KeyValueVaultStore<S> {\n return buildStoreCore(schema, core, options).store;\n}\n\n/** Builds a document store: the key-value surface plus atomic `batch()`. */\nexport function buildDocumentStore<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options: DocumentStoreOptions<S>,\n): DocumentVaultStore<S> {\n const { disposed, notifyMutation, store, validate } = buildStoreCore(schema, core, options);\n const batch = options.createBatch({ notifyMutation, validate });\n\n const runBatch = <K extends keyof S & string, R>(\n tables: readonly K[],\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n ): Promise<R> => {\n if (disposed()) return Promise.reject(new VaultDisposedError());\n\n return batch(tables, fn);\n };\n\n // Read-modify-write helpers stay atomic on document stores by running inside a\n // single-table batch instead of the plain read-then-write pair.\n return Object.assign(store, {\n batch: runBatch,\n deleteMany<K extends keyof S & string>(table: K, keys: readonly KeyOf<S, K>[]): Promise<number> {\n return runBatch([table], (tx) => tx.deleteMany(table, keys));\n },\n update<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n changes: Partial<RecordOf<S, K>>,\n ttl?: number,\n ): Promise<RecordOf<S, K> | undefined> {\n return runBatch([table], (tx) => tx.update(table, key, changes, ttl));\n },\n upsert<K extends keyof S & string>(\n table: K,\n key: KeyOf<S, K>,\n apply: (existing: RecordOf<S, K> | undefined) => RecordOf<S, K>,\n ttl?: number,\n ): Promise<RecordOf<S, K>> {\n return runBatch([table], (tx) => tx.upsert(table, key, apply, ttl));\n },\n }) as DocumentVaultStore<S>;\n}\n"],"mappings":"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"}