@vielzeug/vault 2.3.1 → 2.5.0

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 (72) 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 +2 -2
  4. package/dist/adapter-core.d.ts.map +1 -1
  5. package/dist/adapter-core.js +66 -116
  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 +3 -3
  10. package/dist/adapters/indexeddb.d.ts.map +1 -1
  11. package/dist/adapters/indexeddb.js +93 -102
  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 +1 -3
  16. package/dist/adapters/memory.d.ts.map +1 -1
  17. package/dist/adapters/memory.js +24 -105
  18. package/dist/adapters/memory.js.map +1 -1
  19. package/dist/adapters/sqlite.cjs +16 -4
  20. package/dist/adapters/sqlite.cjs.map +1 -1
  21. package/dist/adapters/sqlite.d.ts +2 -5
  22. package/dist/adapters/sqlite.d.ts.map +1 -1
  23. package/dist/adapters/sqlite.js +122 -90
  24. package/dist/adapters/sqlite.js.map +1 -1
  25. package/dist/adapters/webstorage.cjs +1 -1
  26. package/dist/adapters/webstorage.cjs.map +1 -1
  27. package/dist/adapters/webstorage.d.ts.map +1 -1
  28. package/dist/adapters/webstorage.js +59 -66
  29. package/dist/adapters/webstorage.js.map +1 -1
  30. package/dist/index.cjs +1 -1
  31. package/dist/index.d.ts +1 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -3
  34. package/dist/indexeddb.d.ts +1 -1
  35. package/dist/indexeddb.d.ts.map +1 -1
  36. package/dist/internal.cjs +1 -1
  37. package/dist/internal.cjs.map +1 -1
  38. package/dist/internal.d.ts +1 -1
  39. package/dist/internal.d.ts.map +1 -1
  40. package/dist/internal.js +18 -23
  41. package/dist/internal.js.map +1 -1
  42. package/dist/query.cjs +1 -1
  43. package/dist/query.cjs.map +1 -1
  44. package/dist/query.d.ts +11 -79
  45. package/dist/query.d.ts.map +1 -1
  46. package/dist/query.js +14 -71
  47. package/dist/query.js.map +1 -1
  48. package/dist/sqlite.d.ts +1 -1
  49. package/dist/sqlite.d.ts.map +1 -1
  50. package/dist/types.cjs.map +1 -1
  51. package/dist/types.d.ts +1 -31
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/types.js.map +1 -1
  54. package/dist/vault.cjs +1 -1
  55. package/dist/vault.cjs.map +1 -1
  56. package/dist/vault.iife.js +1 -1
  57. package/dist/vault.iife.js.map +1 -1
  58. package/dist/vault.js +1 -1
  59. package/dist/vault.js.map +1 -1
  60. package/package.json +2 -1
  61. package/dist/_dev.cjs +0 -2
  62. package/dist/_dev.cjs.map +0 -1
  63. package/dist/_dev.d.ts +0 -2
  64. package/dist/_dev.d.ts.map +0 -1
  65. package/dist/_dev.js +0 -6
  66. package/dist/_dev.js.map +0 -1
  67. package/dist/prune.cjs +0 -2
  68. package/dist/prune.cjs.map +0 -1
  69. package/dist/prune.d.ts +0 -21
  70. package/dist/prune.d.ts.map +0 -1
  71. package/dist/prune.js +0 -16
  72. package/dist/prune.js.map +0 -1
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./ttl.cjs"),n=require("./internal.cjs"),r=require("./query.cjs");var i=typeof performance<`u`?()=>performance.now():()=>Date.now();function a(t){if(t.length===0)throw new e.VaultError(`batch: declare at least one table`)}function o(e,n,r){return r!==void 0&&t.assertPositiveFinite(r,`put/putAll`),r??e[n].defaultTtl}function s(t,r,i,a,o){let s=n.getRecordKey(t,r,a);if(s!==i)throw new e.VaultError(`${o}: key field "${t[r].key}" must be "${String(i)}" but got "${String(s)}" in table "${r}"`)}function c(e,t,n){return e.getMany?e.getMany(t,n):Promise.all(n.map(n=>e.get(t,n)))}function l(e,t,r,i){let a=r[e],o=a.key,s=a.indexes?new Set(a.indexes):void 0;return{deleteMany:async a=>{if(a.length===0)return 0;let o=a.map(t=>n.getRecordKey(r,e,t)),s=await t.deleteMany(e,o);return s>0&&i(e),s},getIndexRange:t.getByIndexRange?(n,r)=>t.getByIndexRange(e,n,r):void 0,getRange:t.getByKeyRange?n=>t.getByKeyRange(e,n):void 0,indexedFields:s,keyField:o,source:()=>t.getAll(e)}}function u(t,n,i,a,u){let d=a??((e,t)=>t),f=u?t=>{if(!u.has(t))throw new e.VaultScopeError(`table "${t}" is not part of this batch scope`)}:e=>{};return{async clear(e){f(e);let t=await n.count(e);await n.clear(e),t>0&&i(e)},async count(e){return f(e),n.count(e)},async delete(e,t){f(e);let r=await n.delete(e,t);return r&&i(e),r},async deleteMany(e,t){f(e);let r=await n.deleteMany(e,t);return r>0&&i(e),r},async entries(e){f(e);let r=await n.getAll(e),i=t[e].key;return r.map(e=>[e[i],e])},async get(e,t){return f(e),n.get(e,t)},async getAll(e){return f(e),n.getAll(e)},async getMany(e,t){return f(e),c(n,e,t)},async getOrDefault(e,r,a,c){f(e);let l=await n.get(e,r);if(l!==void 0)return l;let u=a();return s(t,e,r,u,`getOrDefault: defaultFn()`),await n.put(e,d(e,u),o(t,e,c)),i(e),u},async has(e,t){return f(e),n.has(e,t)},async isEmpty(e){return f(e),await n.count(e)===0},async keys(e,r){if(f(e),r){let i=await n.getAll(e),a=t[e].key;return i.filter(r).map(e=>e[a])}if(n.getAllKeys)return n.getAllKeys(e);let i=await n.getAll(e),a=t[e].key;return i.map(e=>e[a])},async put(e,r,a){f(e),await n.put(e,d(e,r),o(t,e,a)),i(e)},async putAll(e,r,a){if(f(e),r.length===0)return;let s=r.map(t=>d(e,t));await n.putAll(e,s,o(t,e,a)),i(e)},query(e){return f(e),r.createQueryBuilder(l(e,n,t,i))},async update(e,r,a,c){f(e);let l=await n.get(e,r);if(!l)return;let u={...l,...a};return s(t,e,r,u,`update`),await n.put(e,d(e,u),o(t,e,c)),i(e),u},async upsert(e,r,a,c){f(e);let l=a(await n.get(e,r));return s(t,e,r,l,`upsert: fn()`),await n.put(e,d(e,l),o(t,e,c)),i(e),l}}}function d(t,a,o){let{logger:s,onMetrics:c,validators:d}=o??{},f=n.createObserverHub(e=>a.getAll(e),s?e=>s.error(`[vault] observer notification failed`,e instanceof Error?e:Error(String(e))):void 0),p=e=>{f.notify(e),o?.onMutation?.(e)},m=e=>{f.notify(e)},h=a.pruneAllExpired?.bind(a),g=a.getRawCount?.bind(a),_=o?.onCrossTabMessage?.(m)??void 0,v=(t,n)=>{let r=d?.[t];if(!r)return n;try{return r.parse(n)}catch(n){throw new e.VaultError(`validation failed for table "${t}"`,{cause:n})}},y=(e,t,n)=>{if(!c)return n();let r=i();return n().finally(()=>c({duration:i()-r,operation:t,table:e}))};o?.onTransactions?.({notifyMutation:p,validate:v});let b=u(t,a,p,v),x=new AbortController,S=!1,C=()=>{if(S)throw new e.VaultDisposedError},w={async clear(e){C(),await y(e,`clear`,()=>b.clear(e))},async count(e){return C(),y(e,`count`,()=>a.count(e))},async debug(){C();let e=Object.keys(t);return{tables:await Promise.all(e.map(async e=>{let t=g?await g(e):void 0,n=await a.count(e);return{expiredCount:t===void 0?0:Math.max(0,t-n),name:e,recordCount:n}}))}},async delete(e,t){return C(),y(e,`delete`,()=>b.delete(e,t))},async deleteMany(e,t){return C(),y(e,`deleteMany`,()=>b.deleteMany(e,t))},get disposalSignal(){return x.signal},async dispose(){S||(S=!0,x.abort(),_?.(),f.dispose(),await a.dispose?.())},get disposed(){return S},async entries(e){return C(),y(e,`entries`,()=>b.entries(e))},async get(e,t){return C(),y(e,`get`,()=>b.get(e,t))},async getAll(e){return C(),y(e,`getAll`,()=>b.getAll(e))},async getMany(e,t){return C(),y(e,`getMany`,()=>b.getMany(e,t))},async getOrDefault(e,t,n,r){return C(),y(e,`getOrDefault`,()=>b.getOrDefault(e,t,n,r))},async has(e,t){return C(),y(e,`has`,()=>b.has(e,t))},async isEmpty(e){return C(),y(e,`isEmpty`,async()=>await a.count(e)===0)},async keys(e,t){return C(),y(e,`keys`,()=>b.keys(e,t))},observe(e,t,n){return C(),f.observe(e,t,n)},async pruneExpired(){if(C(),h)return await h();let e=Object.keys(t),n=await Promise.all(e.map(async e=>[e,await a.pruneExpiredInTable(e)]));return Object.fromEntries(n)},async put(e,t,n){C(),await y(e,`put`,()=>b.put(e,t,n))},async putAll(e,t,n){C(),await y(e,`putAll`,()=>b.putAll(e,t,n))},query(e){C();let n=l(e,a,t,p);return c?r.createQueryBuilder({...n,deleteMany:n.deleteMany?t=>y(e,`queryDelete`,()=>n.deleteMany(t)):void 0,source:()=>y(e,`query`,()=>a.getAll(e))}):r.createQueryBuilder(n)},async[Symbol.asyncDispose](){await w.dispose()},async update(e,t,n,r){return C(),y(e,`update`,()=>b.update(e,t,n,r))},async upsert(e,t,n,r){return C(),y(e,`upsert`,()=>b.upsert(e,t,n,r))}};return w}function f(e,t){return Object.assign(e,{batch:t})}exports.assertBatchTables=a,exports.buildAdapterOps=d,exports.buildTxContext=u,exports.withIndexedDbTransactions=f;
1
+ const e=require("./errors.cjs"),t=require("./ttl.cjs"),n=require("./internal.cjs"),r=require("./query.cjs");function i(t){if(t.length===0)throw new e.VaultError(`batch: declare at least one table`)}function a(e,n,r){return r!==void 0&&t.assertPositiveFinite(r,`put/putAll`),r??e[n].defaultTtl}function o(t,r,i,a,o){let s=n.getRecordKey(t,r,a);if(s!==i)throw new e.VaultError(`${o}: key field "${t[r].key}" must be "${String(i)}" but got "${String(s)}" in table "${r}"`)}function s(e,t,n){return e.getMany?e.getMany(t,n):Promise.all(n.map(n=>e.get(t,n)))}function c(e,t,r,i){return{deleteMany:async a=>{if(a.length===0)return 0;let o=a.map(t=>n.getRecordKey(r,e,t)),s=await t.deleteMany(e,o);return s>0&&i(e),s},source:()=>t.getAll(e)}}function l(t,n,i,l,u){let d=l??((e,t)=>t),f=u?t=>{if(!u.has(t))throw new e.VaultScopeError(`table "${t}" is not part of this batch scope`)}:e=>{};return{async clear(e){f(e);let t=await n.count(e);await n.clear(e),t>0&&i(e)},async count(e){return f(e),n.count(e)},async delete(e,t){f(e);let r=await n.delete(e,t);return r&&i(e),r},async deleteMany(e,t){f(e);let r=await n.deleteMany(e,t);return r>0&&i(e),r},async get(e,t){return f(e),n.get(e,t)},async getAll(e){return f(e),n.getAll(e)},async getMany(e,t){return f(e),s(n,e,t)},async has(e,t){return f(e),n.has(e,t)},async isEmpty(e){return f(e),await n.count(e)===0},async keys(e,r){if(f(e),r){let i=await n.getAll(e),a=t[e].key;return i.filter(r).map(e=>e[a])}if(n.getAllKeys)return n.getAllKeys(e);let i=await n.getAll(e),a=t[e].key;return i.map(e=>e[a])},async put(e,r,o){f(e),await n.put(e,d(e,r),a(t,e,o)),i(e)},async putAll(e,r,o){if(f(e),r.length===0)return;let s=r.map(t=>d(e,t));await n.putAll(e,s,a(t,e,o)),i(e)},query(e){return f(e),r.createQueryBuilder(c(e,n,t,i))},async update(e,r,s,c){f(e);let l=await n.get(e,r);if(!l)return;let u={...l,...s};return o(t,e,r,u,`update`),await n.put(e,d(e,u),a(t,e,c)),i(e),u},async upsert(e,r,s,c){f(e);let l=s(await n.get(e,r));return o(t,e,r,l,`upsert: fn()`),await n.put(e,d(e,l),a(t,e,c)),i(e),l}}}function u(t,i,a){let{validators:o}=a??{},s=n.createObserverHub(e=>i.getAll(e)),u=e=>{s.notify(e),a?.onMutation?.(e)},d=e=>{s.notify(e)},f=i.pruneAllExpired?.bind(i),p=a?.onCrossTabMessage?.(d)??void 0,m=(t,n)=>{let r=o?.[t];if(!r)return n;try{return r.parse(n)}catch(n){throw new e.VaultError(`validation failed for table "${t}"`,{cause:n})}};a?.onTransactions?.({notifyMutation:u,validate:m});let h=l(t,i,u,m),g=new AbortController,_=!1,v=()=>{if(_)throw new e.VaultDisposedError},y={async clear(e){v(),await h.clear(e)},async count(e){return v(),i.count(e)},async delete(e,t){return v(),h.delete(e,t)},async deleteMany(e,t){return v(),h.deleteMany(e,t)},get disposalSignal(){return g.signal},async dispose(){_||(_=!0,g.abort(),p?.(),s.dispose(),await i.dispose?.())},get disposed(){return _},async get(e,t){return v(),h.get(e,t)},async getAll(e){return v(),h.getAll(e)},async getMany(e,t){return v(),h.getMany(e,t)},async has(e,t){return v(),h.has(e,t)},async isEmpty(e){return v(),await i.count(e)===0},async keys(e,t){return v(),h.keys(e,t)},observe(e,t,n){return v(),s.observe(e,t,n)},async pruneExpired(){if(v(),f)return await f();let e=Object.keys(t),n=await Promise.all(e.map(async e=>[e,await i.pruneExpiredInTable(e)]));return Object.fromEntries(n)},async put(e,t,n){v(),await h.put(e,t,n)},async putAll(e,t,n){v(),await h.putAll(e,t,n)},query(e){return v(),r.createQueryBuilder(c(e,i,t,u))},async[Symbol.asyncDispose](){await y.dispose()},async update(e,t,n,r){return v(),h.update(e,t,n,r)},async upsert(e,t,n,r){return v(),h.upsert(e,t,n,r)}};return y}function d(e,t){return Object.assign(e,{batch:t})}exports.assertBatchTables=i,exports.buildAdapterOps=u,exports.buildTxContext=l,exports.withIndexedDbTransactions=d;
2
2
  //# sourceMappingURL=adapter-core.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.cjs","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder, type NativeRange, type QueryContext } from './query';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n IndexedDbVaultStore,\n KeyOf,\n MetricsEvent,\n RecordOf,\n TransactionContext,\n VaultStore,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/** @internal Full backend protocol implemented by each adapter. A single flat interface — no StorageCore/StorageBackend split. */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n /** Returns live (non-expired) record count for the table. */\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: 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 /**\n * Optional: fetch all live primary keys without materialising full records.\n * When present, `keys()` uses this instead of `getAll()` + key extraction.\n * For tables with TTL, implementations may still fall back to `getAll()` to\n * filter expired keys accurately.\n */\n getAllKeys?<T extends K>(table: T): Promise<KeyOf<S, T>[]>;\n /**\n * Optional secondary-index range fetch. When present, `query()` activates `getIndexRange` in\n * the QueryContext so that a leading `equals`, `between`, or `startsWith` on an indexed field\n * uses an IDB index instead of a full-table scan.\n */\n getByIndexRange?<T extends K>(table: T, field: string, range: NativeRange): Promise<RecordOf<S, T>[]>;\n /**\n * Optional primary-key range fetch for push-down queries. When present, `query()` activates\n * `getRange` in the QueryContext so that a leading `equals`, `between`, or `startsWith` on the\n * key field avoids a full-table scan. Falls back to `getAll` when absent.\n */\n getByKeyRange?<T extends K>(table: T, range: NativeRange): Promise<RecordOf<S, T>[]>;\n /**\n * Optional: fetch multiple records by key in a single operation. Preserves key order; missing keys yield\n * `undefined`. Falls back to N individual `get` calls when absent.\n */\n getMany?<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n /** Raw record count including TTL-expired entries. Used only for debug(). */\n getRawCount?<T extends K>(table: T): Promise<number>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\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: 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\nconst getTimestamp: () => number = typeof performance !== 'undefined' ? () => performance.now() : () => Date.now();\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\nfunction getManyWithFallback<S extends AnySchema, K extends keyof S & string>(\n core: Pick<StorageBackend<S, K>, 'get' | 'getMany'>,\n table: K,\n keys: KeyOf<S, K>[],\n): Promise<Array<RecordOf<S, K> | undefined>> {\n if (core.getMany) return core.getMany(table, keys);\n\n return Promise.all(keys.map((k) => core.get(table, k)));\n}\n\nfunction buildQueryCtx<S extends AnySchema, K extends keyof S & string>(\n table: K,\n core: Pick<StorageBackend<S, K>, 'deleteMany' | 'getAll' | 'getByIndexRange' | 'getByKeyRange'>,\n schema: S,\n onMutate: (t: K) => void,\n): QueryContext<RecordOf<S, K>> {\n const entry = schema[table];\n const keyField = entry.key;\n const indexedFields = entry.indexes ? new Set(entry.indexes) : undefined;\n\n const deleteManyFn = async (records: RecordOf<S, K>[]): Promise<number> => {\n if (records.length === 0) return 0;\n\n const keys = records.map((r) => getRecordKey(schema, table, r));\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\n return deleted;\n };\n\n return {\n deleteMany: deleteManyFn,\n getIndexRange: core.getByIndexRange\n ? (field, range) => core.getByIndexRange!(table, field, range) as Promise<RecordOf<S, K>[]>\n : undefined,\n getRange: core.getByKeyRange\n ? (range) => core.getByKeyRange!(table, range) as Promise<RecordOf<S, K>[]>\n : undefined,\n indexedFields,\n keyField,\n source: () => core.getAll(table),\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): TransactionContext<S, K> {\n const applyValidation = validate ?? ((_: K, v: RecordOf<S, K>) => v);\n\n const checkScope = scope\n ? (t: K): void => {\n if (!scope.has(t)) {\n throw new VaultScopeError(`table \"${t}\" is not part of this batch scope`);\n }\n }\n : (_t: K): void => {};\n\n return {\n async clear(table) {\n checkScope(table);\n\n const live = await core.count(table);\n\n await core.clear(table);\n\n if (live > 0) onMutate(table);\n },\n async count(table) {\n checkScope(table);\n\n return core.count(table);\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\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\n return deleted;\n },\n async entries(table) {\n checkScope(table);\n\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.map((r) => [(r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>, r]);\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\n return getManyWithFallback(core, table, keys);\n },\n async getOrDefault(table, key, defaultFn, ttl) {\n checkScope(table);\n\n const existing = await core.get(table, key);\n\n if (existing !== undefined) return existing;\n\n const value = defaultFn();\n\n verifyKey(schema, table, key, value, 'getOrDefault: defaultFn()');\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return value;\n },\n async has(table, key) {\n checkScope(table);\n\n return core.has(table, key);\n },\n async isEmpty(table) {\n checkScope(table);\n\n return (await core.count(table)) === 0;\n },\n async keys(table, filter) {\n checkScope(table);\n\n if (filter) {\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.filter(filter).map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }\n\n // R1: prefer native getAllKeys when the backend supports it (e.g. IDB store.getAllKeys).\n if (core.getAllKeys) {\n return core.getAllKeys(table);\n }\n\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n checkScope(table);\n\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\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\n return createQueryBuilder(buildQueryCtx(table, core, schema, onMutate));\n },\n async update(table, key, changes, ttl) {\n checkScope(table);\n\n const current = await core.get(table, key);\n\n if (!current) return undefined;\n\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n\n verifyKey(schema, table, key, merged, 'update');\n await core.put(table, applyValidation(table, merged), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return merged;\n },\n async upsert(table, key, fn, ttl) {\n checkScope(table);\n\n const existing = await core.get(table, key);\n const value = fn(existing);\n\n verifyKey(schema, table, key, value, 'upsert: fn()');\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return value;\n },\n };\n}\n\n/* -------------------- buildAdapterOps -------------------- */\n\n/** Builds the portable surface first; IndexedDB opts into transactions at its factory boundary. */\nexport function buildAdapterOps<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: BaseAdapterOptions<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): VaultStore<S> {\n const { logger, onMetrics, validators } = options ?? {};\n\n const observers = createObserverHub<S>(\n (table) => core.getAll(table),\n logger\n ? (err) =>\n logger.error('[vault] observer notification failed', err instanceof Error ? err : new Error(String(err)))\n : undefined,\n );\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 // R6: Resolve optional backend capabilities once at construction — no per-call checks.\n const pruneAll = core.pruneAllExpired?.bind(core);\n const rawCountFn = core.getRawCount?.bind(core);\n\n const disconnectExternal = options?.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const validator = validators?.[table];\n\n if (!validator) return value;\n\n try {\n return validator.parse(value) as RecordOf<S, K>;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n\n /* Timed wrapper — only wraps when onMetrics is present, otherwise zero overhead. */\n const timed = <T>(table: string, op: MetricsEvent['operation'], fn: () => Promise<T>): Promise<T> => {\n if (!onMetrics) return fn();\n\n const start = getTimestamp();\n\n return fn().finally(() => onMetrics({ duration: getTimestamp() - start, operation: op, table }));\n };\n\n options?.onTransactions?.({ notifyMutation, validate });\n\n const txCtx = buildTxContext<S, keyof S & string>(schema, core, 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: VaultStore<S> = {\n async clear(table) {\n checkDisposed();\n await timed(table, 'clear', () => txCtx.clear(table));\n },\n\n async count(table) {\n checkDisposed();\n\n return timed(table, 'count', () => core.count(table));\n },\n\n async debug() {\n checkDisposed();\n\n const tableNames = Object.keys(schema) as Array<keyof S & string>;\n const entries = await Promise.all(\n tableNames.map(async (name) => {\n // getRawCount must be called before core.count() because count() has\n // lazy-eviction side effects (deletes expired entries from the store).\n const raw = rawCountFn ? await rawCountFn(name) : undefined;\n const live = await core.count(name);\n\n const expiredCount = raw !== undefined ? Math.max(0, raw - live) : 0;\n\n return { expiredCount, name, recordCount: live };\n }),\n );\n\n return { tables: entries } as DebugInfo<S>;\n },\n\n async delete(table, key) {\n checkDisposed();\n\n return timed(table, 'delete', () => txCtx.delete(table, key));\n // count cache invalidated by notifyMutation inside txCtx.delete\n },\n\n async deleteMany(table, keys) {\n checkDisposed();\n\n return timed(table, 'deleteMany', () => txCtx.deleteMany(table, keys));\n // count cache invalidated by notifyMutation inside txCtx.deleteMany\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 entries(table) {\n checkDisposed();\n\n return timed(table, 'entries', () => txCtx.entries(table));\n },\n\n async get(table, key) {\n checkDisposed();\n\n return timed(table, 'get', () => txCtx.get(table, key));\n },\n\n async getAll(table) {\n checkDisposed();\n\n return timed(table, 'getAll', () => txCtx.getAll(table));\n },\n\n async getMany(table, keys) {\n checkDisposed();\n\n return timed(table, 'getMany', () => txCtx.getMany(table, keys));\n },\n\n async getOrDefault(table, key, defaultFn, ttl) {\n checkDisposed();\n\n return timed(table, 'getOrDefault', () => txCtx.getOrDefault(table, key, defaultFn, ttl));\n },\n\n async has(table, key) {\n checkDisposed();\n\n return timed(table, 'has', () => txCtx.has(table, key));\n },\n\n async isEmpty(table) {\n checkDisposed();\n\n return timed(table, 'isEmpty', async () => (await core.count(table)) === 0);\n },\n\n async keys(table, filter) {\n checkDisposed();\n\n return timed(table, 'keys', () => txCtx.keys(table, filter));\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 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 return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async put(table, value, ttl) {\n checkDisposed();\n await timed(table, 'put', () => txCtx.put(table, value, ttl));\n // count cache invalidated via notifyMutation inside txCtx.put\n },\n\n async putAll(table, values, ttl) {\n checkDisposed();\n await timed(table, 'putAll', () => txCtx.putAll(table, values, ttl));\n // count cache invalidated via notifyMutation inside txCtx.putAll\n },\n\n query(table) {\n checkDisposed();\n\n const ctx = buildQueryCtx(table, core, schema, notifyMutation);\n\n if (onMetrics) {\n return createQueryBuilder({\n ...ctx,\n deleteMany: ctx.deleteMany\n ? (records) => timed(table, 'queryDelete', () => ctx.deleteMany!(records))\n : undefined,\n source: () => timed(table, 'query', () => core.getAll(table)),\n });\n }\n\n return createQueryBuilder(ctx);\n },\n\n async [Symbol.asyncDispose]() {\n await adapter.dispose();\n },\n\n async update(table, key, changes, ttl) {\n checkDisposed();\n\n return timed(table, 'update', () => txCtx.update(table, key, changes, ttl));\n },\n\n async upsert(table, key, fn, ttl) {\n checkDisposed();\n\n return timed(table, 'upsert', () => txCtx.upsert(table, key, fn, ttl));\n },\n };\n\n return adapter;\n}\n\n/** IndexedDB is the only backend that can bind this callback to one native transaction. */\nexport function withIndexedDbTransactions<S extends AnySchema>(\n store: VaultStore<S>,\n batch: BatchImpl<S>,\n): IndexedDbVaultStore<S> {\n return Object.assign(store, { batch }) as IndexedDbVaultStore<S>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"adapter-core.cjs","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder, type QueryContext } from './query';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n VaultStore,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/** @internal Full backend protocol implemented by each adapter. A single flat interface — no StorageCore/StorageBackend split. */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n /** Returns live (non-expired) record count for the table. */\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: 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 /**\n * Optional: fetch all live primary keys without materialising full records.\n * When present, `keys()` uses this instead of `getAll()` + key extraction.\n * For tables with TTL, implementations may still fall back to `getAll()` to\n * filter expired keys accurately.\n */\n getAllKeys?<T extends K>(table: T): Promise<KeyOf<S, T>[]>;\n /**\n * Optional: fetch multiple records by key in a single operation. Preserves key order; missing keys yield\n * `undefined`. Falls back to N individual `get` calls when absent.\n */\n getMany?<T extends K>(table: T, keys: 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: 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: 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\nfunction getManyWithFallback<S extends AnySchema, K extends keyof S & string>(\n core: Pick<StorageBackend<S, K>, 'get' | 'getMany'>,\n table: K,\n keys: KeyOf<S, K>[],\n): Promise<Array<RecordOf<S, K> | undefined>> {\n if (core.getMany) return core.getMany(table, keys);\n\n return Promise.all(keys.map((k) => core.get(table, k)));\n}\n\nfunction buildQueryCtx<S extends AnySchema, K extends keyof S & string>(\n table: K,\n core: Pick<StorageBackend<S, K>, 'deleteMany' | 'getAll'>,\n schema: S,\n onMutate: (t: K) => void,\n): QueryContext<RecordOf<S, K>> {\n const deleteManyFn = async (records: RecordOf<S, K>[]): Promise<number> => {\n if (records.length === 0) return 0;\n\n const keys = records.map((r) => getRecordKey(schema, table, r));\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\n return deleted;\n };\n\n return {\n deleteMany: deleteManyFn,\n source: () => core.getAll(table),\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): TransactionContext<S, K> {\n const applyValidation = validate ?? ((_: K, v: RecordOf<S, K>) => v);\n\n const checkScope = scope\n ? (t: K): void => {\n if (!scope.has(t)) {\n throw new VaultScopeError(`table \"${t}\" is not part of this batch scope`);\n }\n }\n : (_t: K): void => {};\n\n return {\n async clear(table) {\n checkScope(table);\n\n const live = await core.count(table);\n\n await core.clear(table);\n\n if (live > 0) onMutate(table);\n },\n async count(table) {\n checkScope(table);\n\n return core.count(table);\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\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\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\n return getManyWithFallback(core, table, keys);\n },\n async has(table, key) {\n checkScope(table);\n\n return core.has(table, key);\n },\n async isEmpty(table) {\n checkScope(table);\n\n return (await core.count(table)) === 0;\n },\n async keys(table, filter) {\n checkScope(table);\n\n if (filter) {\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.filter(filter).map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }\n\n // R1: prefer native getAllKeys when the backend supports it (e.g. IDB store.getAllKeys).\n if (core.getAllKeys) {\n return core.getAllKeys(table);\n }\n\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n checkScope(table);\n\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\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\n return createQueryBuilder(buildQueryCtx(table, core, schema, onMutate));\n },\n async update(table, key, changes, ttl) {\n checkScope(table);\n\n const current = await core.get(table, key);\n\n if (!current) return undefined;\n\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n\n verifyKey(schema, table, key, merged, 'update');\n await core.put(table, applyValidation(table, merged), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return merged;\n },\n async upsert(table, key, fn, ttl) {\n checkScope(table);\n\n const existing = await core.get(table, key);\n const value = fn(existing);\n\n verifyKey(schema, table, key, value, 'upsert: fn()');\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return value;\n },\n };\n}\n\n/* -------------------- buildAdapterOps -------------------- */\n\n/** Builds the portable surface first; IndexedDB opts into transactions at its factory boundary. */\nexport function buildAdapterOps<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: BaseAdapterOptions<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): VaultStore<S> {\n const { validators } = 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 // R6: Resolve optional backend capabilities once at construction — no per-call checks.\n const pruneAll = core.pruneAllExpired?.bind(core);\n\n const disconnectExternal = options?.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const validator = validators?.[table];\n\n if (!validator) return value;\n\n try {\n return validator.parse(value) as RecordOf<S, K>;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n\n options?.onTransactions?.({ notifyMutation, validate });\n\n const txCtx = buildTxContext<S, keyof S & string>(schema, core, 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: VaultStore<S> = {\n async clear(table) {\n checkDisposed();\n await txCtx.clear(table);\n },\n\n async count(table) {\n checkDisposed();\n\n return core.count(table);\n },\n\n async delete(table, key) {\n checkDisposed();\n\n return txCtx.delete(table, key);\n },\n\n async deleteMany(table, keys) {\n checkDisposed();\n\n return txCtx.deleteMany(table, keys);\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 txCtx.get(table, key);\n },\n\n async getAll(table) {\n checkDisposed();\n\n return txCtx.getAll(table);\n },\n\n async getMany(table, keys) {\n checkDisposed();\n\n return txCtx.getMany(table, keys);\n },\n\n async has(table, key) {\n checkDisposed();\n\n return txCtx.has(table, key);\n },\n\n async isEmpty(table) {\n checkDisposed();\n\n return (await core.count(table)) === 0;\n },\n\n async keys(table, filter) {\n checkDisposed();\n\n return txCtx.keys(table, filter);\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 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 return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async put(table, value, ttl) {\n checkDisposed();\n await txCtx.put(table, value, ttl);\n },\n\n async putAll(table, values, ttl) {\n checkDisposed();\n await txCtx.putAll(table, values, ttl);\n },\n\n query(table) {\n checkDisposed();\n\n return createQueryBuilder(buildQueryCtx(table, core, schema, notifyMutation));\n },\n\n async [Symbol.asyncDispose]() {\n await adapter.dispose();\n },\n\n async update(table, key, changes, ttl) {\n checkDisposed();\n\n return txCtx.update(table, key, changes, ttl);\n },\n\n async upsert(table, key, fn, ttl) {\n checkDisposed();\n\n return txCtx.upsert(table, key, fn, ttl);\n },\n };\n\n return adapter;\n}\n\n/** IndexedDB is the only backend that can bind this callback to one native transaction. */\nexport function withIndexedDbTransactions<S extends AnySchema>(\n store: VaultStore<S>,\n batch: BatchImpl<S>,\n): TransactionalVaultStore<S> {\n return Object.assign(store, { batch }) as TransactionalVaultStore<S>;\n}\n"],"mappings":"4GA+DA,SAAgB,EAAkB,EAAiC,CACjE,GAAI,EAAO,SAAW,EAAG,MAAM,IAAI,EAAA,WAAW,mCAAmC,CACnF,CAEA,SAAS,EACP,EACA,EACA,EACoB,CAGpB,OAFI,IAAQ,IAAA,IAAW,EAAA,qBAAqB,EAAK,YAAY,EAEtD,GAAO,EAAO,EAAM,CAAC,UAC9B,CAEA,SAAS,EACP,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,CAEA,SAAS,EACP,EACA,EACA,EAC4C,CAG5C,OAFI,EAAK,QAAgB,EAAK,QAAQ,EAAO,CAAI,EAE1C,QAAQ,IAAI,EAAK,IAAK,GAAM,EAAK,IAAI,EAAO,CAAC,CAAC,CAAC,CACxD,CAEA,SAAS,EACP,EACA,EACA,EACA,EAC8B,CAY9B,MAAO,CACL,WAAY,KAZc,IAA+C,CACzE,GAAI,EAAQ,SAAW,EAAG,MAAO,GAEjC,IAAM,EAAO,EAAQ,IAAK,GAAM,EAAA,aAAa,EAAQ,EAAO,CAAC,CAAC,EACxD,EAAU,MAAM,EAAK,WAAW,EAAO,CAAI,EAIjD,OAFI,EAAU,GAAG,EAAS,CAAK,EAExB,CACT,EAIE,WAAc,EAAK,OAAO,CAAK,CACjC,CACF,CAIA,SAAgB,EACd,EACA,EACA,EACA,EACA,EAC0B,CAC1B,IAAM,EAAkB,KAAc,EAAM,IAAsB,GAE5D,EAAa,EACd,GAAe,CACd,GAAI,CAAC,EAAM,IAAI,CAAC,EACd,MAAM,IAAI,EAAA,gBAAgB,UAAU,EAAE,kCAAkC,CAE5E,EACC,GAAgB,CAAC,EAEtB,MAAO,CACL,MAAM,MAAM,EAAO,CACjB,EAAW,CAAK,EAEhB,IAAM,EAAO,MAAM,EAAK,MAAM,CAAK,EAEnC,MAAM,EAAK,MAAM,CAAK,EAElB,EAAO,GAAG,EAAS,CAAK,CAC9B,EACA,MAAM,MAAM,EAAO,CAGjB,OAFA,EAAW,CAAK,EAET,EAAK,MAAM,CAAK,CACzB,EACA,MAAM,OAAO,EAAO,EAAK,CACvB,EAAW,CAAK,EAEhB,IAAM,EAAU,MAAM,EAAK,OAAO,EAAO,CAAG,EAI5C,OAFI,GAAS,EAAS,CAAK,EAEpB,CACT,EACA,MAAM,WAAW,EAAO,EAAM,CAC5B,EAAW,CAAK,EAEhB,IAAM,EAAU,MAAM,EAAK,WAAW,EAAO,CAAI,EAIjD,OAFI,EAAU,GAAG,EAAS,CAAK,EAExB,CACT,EACA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAW,CAAK,EAET,EAAK,IAAI,EAAO,CAAG,CAC5B,EACA,MAAM,OAAO,EAAO,CAGlB,OAFA,EAAW,CAAK,EAET,EAAK,OAAO,CAAK,CAC1B,EACA,MAAM,QAAQ,EAAO,EAAM,CAGzB,OAFA,EAAW,CAAK,EAET,EAAoB,EAAM,EAAO,CAAI,CAC9C,EACA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAW,CAAK,EAET,EAAK,IAAI,EAAO,CAAG,CAC5B,EACA,MAAM,QAAQ,EAAO,CAGnB,OAFA,EAAW,CAAK,EAER,MAAM,EAAK,MAAM,CAAK,IAAO,CACvC,EACA,MAAM,KAAK,EAAO,EAAQ,CAGxB,GAFA,EAAW,CAAK,EAEZ,EAAQ,CACV,IAAM,EAAU,MAAM,EAAK,OAAO,CAAK,EACjC,EAAW,EAAO,EAAM,CAAC,IAE/B,OAAO,EAAQ,OAAO,CAAM,CAAC,CAAC,IAAK,GAAO,EAA8B,EAAmC,CAC7G,CAGA,GAAI,EAAK,WACP,OAAO,EAAK,WAAW,CAAK,EAG9B,IAAM,EAAU,MAAM,EAAK,OAAO,CAAK,EACjC,EAAW,EAAO,EAAM,CAAC,IAE/B,OAAO,EAAQ,IAAK,GAAO,EAA8B,EAAmC,CAC9F,EACA,MAAM,IAAI,EAAO,EAAO,EAAK,CAC3B,EAAW,CAAK,EAEhB,MAAM,EAAK,IAAI,EAAO,EAAgB,EAAO,CAAK,EAAG,EAAW,EAAQ,EAAO,CAAG,CAAC,EACnF,EAAS,CAAK,CAChB,EACA,MAAM,OAAO,EAAO,EAAQ,EAAK,CAG/B,GAFA,EAAW,CAAK,EAEZ,EAAO,SAAW,EAAG,OAEzB,IAAM,EAAU,EAAO,IAAK,GAAM,EAAgB,EAAO,CAAC,CAAC,EAE3D,MAAM,EAAK,OAAO,EAAO,EAAS,EAAW,EAAQ,EAAO,CAAG,CAAC,EAChE,EAAS,CAAK,CAChB,EACA,MAAM,EAAO,CAGX,OAFA,EAAW,CAAK,EAET,EAAA,mBAAmB,EAAc,EAAO,EAAM,EAAQ,CAAQ,CAAC,CACxE,EACA,MAAM,OAAO,EAAO,EAAK,EAAS,EAAK,CACrC,EAAW,CAAK,EAEhB,IAAM,EAAU,MAAM,EAAK,IAAI,EAAO,CAAG,EAEzC,GAAI,CAAC,EAAS,OAEd,IAAM,EAAS,CAAE,GAAG,EAAS,GAAG,CAAQ,EAMxC,OAJA,EAAU,EAAQ,EAAO,EAAK,EAAQ,QAAQ,EAC9C,MAAM,EAAK,IAAI,EAAO,EAAgB,EAAO,CAAM,EAAG,EAAW,EAAQ,EAAO,CAAG,CAAC,EACpF,EAAS,CAAK,EAEP,CACT,EACA,MAAM,OAAO,EAAO,EAAK,EAAI,EAAK,CAChC,EAAW,CAAK,EAGhB,IAAM,EAAQ,EAAG,MADM,EAAK,IAAI,EAAO,CAAG,CACjB,EAMzB,OAJA,EAAU,EAAQ,EAAO,EAAK,EAAO,cAAc,EACnD,MAAM,EAAK,IAAI,EAAO,EAAgB,EAAO,CAAK,EAAG,EAAW,EAAQ,EAAO,CAAG,CAAC,EACnF,EAAS,CAAK,EAEP,CACT,CACF,CACF,CAKA,SAAgB,EACd,EACA,EACA,EAKe,CACf,GAAM,CAAE,cAAe,GAAW,CAAC,EAE7B,EAAY,EAAA,kBAAsB,GAAU,EAAK,OAAO,CAAK,CAAC,EAE9D,EAAkB,GAAkC,CACxD,EAAU,OAAO,CAAK,EACtB,GAAS,aAAa,CAAK,CAC7B,EAIM,EAAkB,GAAkC,CACxD,EAAU,OAAO,CAAK,CACxB,EAGM,EAAW,EAAK,iBAAiB,KAAK,CAAI,EAE1C,EAAqB,GAAS,oBAAoB,CAAc,GAAK,IAAA,GAErE,GAAwC,EAAU,IAA0C,CAChG,IAAM,EAAY,IAAa,GAE/B,GAAI,CAAC,EAAW,OAAO,EAEvB,GAAI,CACF,OAAO,EAAU,MAAM,CAAK,CAC9B,OAAS,EAAK,CACZ,MAAM,IAAI,EAAA,WAAW,gCAAgC,EAAM,GAAI,CAAE,MAAO,CAAI,CAAC,CAC/E,CACF,EAEA,GAAS,iBAAiB,CAAE,iBAAgB,UAAS,CAAC,EAEtD,IAAM,EAAQ,EAAoC,EAAQ,EAAM,EAAgB,CAAQ,EAElF,EAAoB,IAAI,gBAC1B,EAAW,GAET,MAA4B,CAChC,GAAI,EAAU,MAAM,IAAI,EAAA,kBAC1B,EAEM,EAAyB,CAC7B,MAAM,MAAM,EAAO,CACjB,EAAc,EACd,MAAM,EAAM,MAAM,CAAK,CACzB,EAEA,MAAM,MAAM,EAAO,CAGjB,OAFA,EAAc,EAEP,EAAK,MAAM,CAAK,CACzB,EAEA,MAAM,OAAO,EAAO,EAAK,CAGvB,OAFA,EAAc,EAEP,EAAM,OAAO,EAAO,CAAG,CAChC,EAEA,MAAM,WAAW,EAAO,EAAM,CAG5B,OAFA,EAAc,EAEP,EAAM,WAAW,EAAO,CAAI,CACrC,EAEA,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,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAc,EAEP,EAAM,IAAI,EAAO,CAAG,CAC7B,EAEA,MAAM,OAAO,EAAO,CAGlB,OAFA,EAAc,EAEP,EAAM,OAAO,CAAK,CAC3B,EAEA,MAAM,QAAQ,EAAO,EAAM,CAGzB,OAFA,EAAc,EAEP,EAAM,QAAQ,EAAO,CAAI,CAClC,EAEA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAc,EAEP,EAAM,IAAI,EAAO,CAAG,CAC7B,EAEA,MAAM,QAAQ,EAAO,CAGnB,OAFA,EAAc,EAEN,MAAM,EAAK,MAAM,CAAK,IAAO,CACvC,EAEA,MAAM,KAAK,EAAO,EAAQ,CAGxB,OAFA,EAAc,EAEP,EAAM,KAAK,EAAO,CAAM,CACjC,EAEA,QAAQ,EAAO,EAAU,EAAM,CAG7B,OAFA,EAAc,EAEP,EAAU,QAAQ,EAAO,EAAU,CAAI,CAChD,EAEA,MAAM,cAAe,CAGnB,GAFA,EAAc,EAEV,EAGF,OAAO,MAFc,EAAS,EAKhC,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,OAAO,OAAO,YAAY,CAAK,CACjC,EAEA,MAAM,IAAI,EAAO,EAAO,EAAK,CAC3B,EAAc,EACd,MAAM,EAAM,IAAI,EAAO,EAAO,CAAG,CACnC,EAEA,MAAM,OAAO,EAAO,EAAQ,EAAK,CAC/B,EAAc,EACd,MAAM,EAAM,OAAO,EAAO,EAAQ,CAAG,CACvC,EAEA,MAAM,EAAO,CAGX,OAFA,EAAc,EAEP,EAAA,mBAAmB,EAAc,EAAO,EAAM,EAAQ,CAAc,CAAC,CAC9E,EAEA,MAAO,OAAO,eAAgB,CAC5B,MAAM,EAAQ,QAAQ,CACxB,EAEA,MAAM,OAAO,EAAO,EAAK,EAAS,EAAK,CAGrC,OAFA,EAAc,EAEP,EAAM,OAAO,EAAO,EAAK,EAAS,CAAG,CAC9C,EAEA,MAAM,OAAO,EAAO,EAAK,EAAI,EAAK,CAGhC,OAFA,EAAc,EAEP,EAAM,OAAO,EAAO,EAAK,EAAI,CAAG,CACzC,CACF,EAEA,OAAO,CACT,CAGA,SAAgB,EACd,EACA,EAC4B,CAC5B,OAAO,OAAO,OAAO,EAAO,CAAE,OAAM,CAAC,CACvC"}
@@ -1,4 +1,4 @@
1
- import type { AnySchema, BaseAdapterOptions, IndexedDbVaultStore, RecordOf, TransactionContext, VaultStore } from './types';
1
+ import type { AnySchema, BaseAdapterOptions, RecordOf, TransactionalVaultStore, TransactionContext, VaultStore } from './types';
2
2
  export declare function assertBatchTables(tables: readonly string[]): void;
3
3
  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>): TransactionContext<S, K>;
4
4
  /** Builds the portable surface first; IndexedDB opts into transactions at its factory boundary. */
@@ -8,5 +8,5 @@ export declare function buildAdapterOps<S extends AnySchema>(schema: S, core: St
8
8
  onTransactions?: (deps: BatchDeps<S>) => void;
9
9
  }): VaultStore<S>;
10
10
  /** IndexedDB is the only backend that can bind this callback to one native transaction. */
11
- export declare function withIndexedDbTransactions<S extends AnySchema>(store: VaultStore<S>, batch: BatchImpl<S>): IndexedDbVaultStore<S>;
11
+ export declare function withIndexedDbTransactions<S extends AnySchema>(store: VaultStore<S>, batch: BatchImpl<S>): TransactionalVaultStore<S>;
12
12
  //# sourceMappingURL=adapter-core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.d.ts","sourceRoot":"","sources":["../src/adapter-core.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAElB,mBAAmB,EAGnB,QAAQ,EACR,kBAAkB,EAClB,UAAU,EACX,MAAM,SAAS,CAAC;AAmEjB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEjE;AA2ED,wBAAgB,cAAc,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAC5E,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC5B,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3E,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC1B,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAiK1B;AAID,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,EACjD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAChC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CAC/C,GACA,UAAU,CAAC,CAAC,CAAC,CAuPf;AAED,2FAA2F;AAC3F,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,SAAS,EAC3D,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,mBAAmB,CAAC,CAAC,CAAC,CAExB"}
1
+ {"version":3,"file":"adapter-core.d.ts","sourceRoot":"","sources":["../src/adapter-core.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAElB,QAAQ,EACR,uBAAuB,EACvB,kBAAkB,EAClB,UAAU,EACX,MAAM,SAAS,CAAC;AAmDjB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEjE;AA+DD,wBAAgB,cAAc,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAC5E,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC5B,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3E,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC1B,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CA0I1B;AAID,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,EACjD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAChC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CAC/C,GACA,UAAU,CAAC,CAAC,CAAC,CAuLf;AAED,2FAA2F;AAC3F,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,SAAS,EAC3D,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,uBAAuB,CAAC,CAAC,CAAC,CAE5B"}
@@ -3,87 +3,69 @@ import { assertPositiveFinite as r } from "./ttl.js";
3
3
  import { createObserverHub as i, getRecordKey as a } from "./internal.js";
4
4
  import { createQueryBuilder as o } from "./query.js";
5
5
  //#region src/adapter-core.ts
6
- var s = typeof performance < "u" ? () => performance.now() : () => Date.now();
7
- function c(e) {
6
+ function s(e) {
8
7
  if (e.length === 0) throw new t("batch: declare at least one table");
9
8
  }
10
- function l(e, t, n) {
9
+ function c(e, t, n) {
11
10
  return n !== void 0 && r(n, "put/putAll"), n ?? e[t].defaultTtl;
12
11
  }
13
- function u(e, n, r, i, o) {
12
+ function l(e, n, r, i, o) {
14
13
  let s = a(e, n, i);
15
14
  if (s !== r) throw new t(`${o}: key field "${e[n].key}" must be "${String(r)}" but got "${String(s)}" in table "${n}"`);
16
15
  }
17
- function d(e, t, n) {
16
+ function u(e, t, n) {
18
17
  return e.getMany ? e.getMany(t, n) : Promise.all(n.map((n) => e.get(t, n)));
19
18
  }
20
- function f(e, t, n, r) {
21
- let i = n[e], o = i.key, s = i.indexes ? new Set(i.indexes) : void 0;
19
+ function d(e, t, n, r) {
22
20
  return {
23
21
  deleteMany: async (i) => {
24
22
  if (i.length === 0) return 0;
25
23
  let o = i.map((t) => a(n, e, t)), s = await t.deleteMany(e, o);
26
24
  return s > 0 && r(e), s;
27
25
  },
28
- getIndexRange: t.getByIndexRange ? (n, r) => t.getByIndexRange(e, n, r) : void 0,
29
- getRange: t.getByKeyRange ? (n) => t.getByKeyRange(e, n) : void 0,
30
- indexedFields: s,
31
- keyField: o,
32
26
  source: () => t.getAll(e)
33
27
  };
34
28
  }
35
- function p(e, t, r, i, a) {
36
- let s = i ?? ((e, t) => t), c = a ? (e) => {
29
+ function f(e, t, r, i, a) {
30
+ let s = i ?? ((e, t) => t), f = a ? (e) => {
37
31
  if (!a.has(e)) throw new n(`table "${e}" is not part of this batch scope`);
38
32
  } : (e) => {};
39
33
  return {
40
34
  async clear(e) {
41
- c(e);
35
+ f(e);
42
36
  let n = await t.count(e);
43
37
  await t.clear(e), n > 0 && r(e);
44
38
  },
45
39
  async count(e) {
46
- return c(e), t.count(e);
40
+ return f(e), t.count(e);
47
41
  },
48
42
  async delete(e, n) {
49
- c(e);
43
+ f(e);
50
44
  let i = await t.delete(e, n);
51
45
  return i && r(e), i;
52
46
  },
53
47
  async deleteMany(e, n) {
54
- c(e);
48
+ f(e);
55
49
  let i = await t.deleteMany(e, n);
56
50
  return i > 0 && r(e), i;
57
51
  },
58
- async entries(n) {
59
- c(n);
60
- let r = await t.getAll(n), i = e[n].key;
61
- return r.map((e) => [e[i], e]);
62
- },
63
52
  async get(e, n) {
64
- return c(e), t.get(e, n);
53
+ return f(e), t.get(e, n);
65
54
  },
66
55
  async getAll(e) {
67
- return c(e), t.getAll(e);
56
+ return f(e), t.getAll(e);
68
57
  },
69
58
  async getMany(e, n) {
70
- return c(e), d(t, e, n);
71
- },
72
- async getOrDefault(n, i, a, o) {
73
- c(n);
74
- let d = await t.get(n, i);
75
- if (d !== void 0) return d;
76
- let f = a();
77
- return u(e, n, i, f, "getOrDefault: defaultFn()"), await t.put(n, s(n, f), l(e, n, o)), r(n), f;
59
+ return f(e), u(t, e, n);
78
60
  },
79
61
  async has(e, n) {
80
- return c(e), t.has(e, n);
62
+ return f(e), t.has(e, n);
81
63
  },
82
64
  async isEmpty(e) {
83
- return c(e), await t.count(e) === 0;
65
+ return f(e), await t.count(e) === 0;
84
66
  },
85
67
  async keys(n, r) {
86
- if (c(n), r) {
68
+ if (f(n), r) {
87
69
  let i = await t.getAll(n), a = e[n].key;
88
70
  return i.filter(r).map((e) => e[a]);
89
71
  }
@@ -92,158 +74,126 @@ function p(e, t, r, i, a) {
92
74
  return i.map((e) => e[a]);
93
75
  },
94
76
  async put(n, i, a) {
95
- c(n), await t.put(n, s(n, i), l(e, n, a)), r(n);
77
+ f(n), await t.put(n, s(n, i), c(e, n, a)), r(n);
96
78
  },
97
79
  async putAll(n, i, a) {
98
- if (c(n), i.length === 0) return;
80
+ if (f(n), i.length === 0) return;
99
81
  let o = i.map((e) => s(n, e));
100
- await t.putAll(n, o, l(e, n, a)), r(n);
82
+ await t.putAll(n, o, c(e, n, a)), r(n);
101
83
  },
102
84
  query(n) {
103
- return c(n), o(f(n, t, e, r));
85
+ return f(n), o(d(n, t, e, r));
104
86
  },
105
87
  async update(n, i, a, o) {
106
- c(n);
107
- let d = await t.get(n, i);
108
- if (!d) return;
109
- let f = {
110
- ...d,
88
+ f(n);
89
+ let u = await t.get(n, i);
90
+ if (!u) return;
91
+ let d = {
92
+ ...u,
111
93
  ...a
112
94
  };
113
- return u(e, n, i, f, "update"), await t.put(n, s(n, f), l(e, n, o)), r(n), f;
95
+ return l(e, n, i, d, "update"), await t.put(n, s(n, d), c(e, n, o)), r(n), d;
114
96
  },
115
97
  async upsert(n, i, a, o) {
116
- c(n);
117
- let d = a(await t.get(n, i));
118
- return u(e, n, i, d, "upsert: fn()"), await t.put(n, s(n, d), l(e, n, o)), r(n), d;
98
+ f(n);
99
+ let u = a(await t.get(n, i));
100
+ return l(e, n, i, u, "upsert: fn()"), await t.put(n, s(n, u), c(e, n, o)), r(n), u;
119
101
  }
120
102
  };
121
103
  }
122
- function m(n, r, a) {
123
- let { logger: c, onMetrics: l, validators: u } = a ?? {}, d = i((e) => r.getAll(e), c ? (e) => c.error("[vault] observer notification failed", e instanceof Error ? e : Error(String(e))) : void 0), m = (e) => {
124
- d.notify(e), a?.onMutation?.(e);
125
- }, h = (e) => {
126
- d.notify(e);
127
- }, g = r.pruneAllExpired?.bind(r), _ = r.getRawCount?.bind(r), v = a?.onCrossTabMessage?.(h) ?? void 0, y = (e, n) => {
128
- let r = u?.[e];
104
+ function p(n, r, a) {
105
+ let { validators: s } = a ?? {}, c = i((e) => r.getAll(e)), l = (e) => {
106
+ c.notify(e), a?.onMutation?.(e);
107
+ }, u = (e) => {
108
+ c.notify(e);
109
+ }, p = r.pruneAllExpired?.bind(r), m = a?.onCrossTabMessage?.(u) ?? void 0, h = (e, n) => {
110
+ let r = s?.[e];
129
111
  if (!r) return n;
130
112
  try {
131
113
  return r.parse(n);
132
114
  } catch (n) {
133
115
  throw new t(`validation failed for table "${e}"`, { cause: n });
134
116
  }
135
- }, b = (e, t, n) => {
136
- if (!l) return n();
137
- let r = s();
138
- return n().finally(() => l({
139
- duration: s() - r,
140
- operation: t,
141
- table: e
142
- }));
143
117
  };
144
118
  a?.onTransactions?.({
145
- notifyMutation: m,
146
- validate: y
119
+ notifyMutation: l,
120
+ validate: h
147
121
  });
148
- let x = p(n, r, m, y), S = new AbortController(), C = !1, w = () => {
149
- if (C) throw new e();
150
- }, T = {
122
+ let g = f(n, r, l, h), _ = new AbortController(), v = !1, y = () => {
123
+ if (v) throw new e();
124
+ }, b = {
151
125
  async clear(e) {
152
- w(), await b(e, "clear", () => x.clear(e));
126
+ y(), await g.clear(e);
153
127
  },
154
128
  async count(e) {
155
- return w(), b(e, "count", () => r.count(e));
156
- },
157
- async debug() {
158
- w();
159
- let e = Object.keys(n);
160
- return { tables: await Promise.all(e.map(async (e) => {
161
- let t = _ ? await _(e) : void 0, n = await r.count(e);
162
- return {
163
- expiredCount: t === void 0 ? 0 : Math.max(0, t - n),
164
- name: e,
165
- recordCount: n
166
- };
167
- })) };
129
+ return y(), r.count(e);
168
130
  },
169
131
  async delete(e, t) {
170
- return w(), b(e, "delete", () => x.delete(e, t));
132
+ return y(), g.delete(e, t);
171
133
  },
172
134
  async deleteMany(e, t) {
173
- return w(), b(e, "deleteMany", () => x.deleteMany(e, t));
135
+ return y(), g.deleteMany(e, t);
174
136
  },
175
137
  get disposalSignal() {
176
- return S.signal;
138
+ return _.signal;
177
139
  },
178
140
  async dispose() {
179
- C || (C = !0, S.abort(), v?.(), d.dispose(), await r.dispose?.());
141
+ v || (v = !0, _.abort(), m?.(), c.dispose(), await r.dispose?.());
180
142
  },
181
143
  get disposed() {
182
- return C;
183
- },
184
- async entries(e) {
185
- return w(), b(e, "entries", () => x.entries(e));
144
+ return v;
186
145
  },
187
146
  async get(e, t) {
188
- return w(), b(e, "get", () => x.get(e, t));
147
+ return y(), g.get(e, t);
189
148
  },
190
149
  async getAll(e) {
191
- return w(), b(e, "getAll", () => x.getAll(e));
150
+ return y(), g.getAll(e);
192
151
  },
193
152
  async getMany(e, t) {
194
- return w(), b(e, "getMany", () => x.getMany(e, t));
195
- },
196
- async getOrDefault(e, t, n, r) {
197
- return w(), b(e, "getOrDefault", () => x.getOrDefault(e, t, n, r));
153
+ return y(), g.getMany(e, t);
198
154
  },
199
155
  async has(e, t) {
200
- return w(), b(e, "has", () => x.has(e, t));
156
+ return y(), g.has(e, t);
201
157
  },
202
158
  async isEmpty(e) {
203
- return w(), b(e, "isEmpty", async () => await r.count(e) === 0);
159
+ return y(), await r.count(e) === 0;
204
160
  },
205
161
  async keys(e, t) {
206
- return w(), b(e, "keys", () => x.keys(e, t));
162
+ return y(), g.keys(e, t);
207
163
  },
208
164
  observe(e, t, n) {
209
- return w(), d.observe(e, t, n);
165
+ return y(), c.observe(e, t, n);
210
166
  },
211
167
  async pruneExpired() {
212
- if (w(), g) return await g();
168
+ if (y(), p) return await p();
213
169
  let e = Object.keys(n), t = await Promise.all(e.map(async (e) => [e, await r.pruneExpiredInTable(e)]));
214
170
  return Object.fromEntries(t);
215
171
  },
216
172
  async put(e, t, n) {
217
- w(), await b(e, "put", () => x.put(e, t, n));
173
+ y(), await g.put(e, t, n);
218
174
  },
219
175
  async putAll(e, t, n) {
220
- w(), await b(e, "putAll", () => x.putAll(e, t, n));
176
+ y(), await g.putAll(e, t, n);
221
177
  },
222
178
  query(e) {
223
- w();
224
- let t = f(e, r, n, m);
225
- return o(l ? {
226
- ...t,
227
- deleteMany: t.deleteMany ? (n) => b(e, "queryDelete", () => t.deleteMany(n)) : void 0,
228
- source: () => b(e, "query", () => r.getAll(e))
229
- } : t);
179
+ return y(), o(d(e, r, n, l));
230
180
  },
231
181
  async [Symbol.asyncDispose]() {
232
- await T.dispose();
182
+ await b.dispose();
233
183
  },
234
184
  async update(e, t, n, r) {
235
- return w(), b(e, "update", () => x.update(e, t, n, r));
185
+ return y(), g.update(e, t, n, r);
236
186
  },
237
187
  async upsert(e, t, n, r) {
238
- return w(), b(e, "upsert", () => x.upsert(e, t, n, r));
188
+ return y(), g.upsert(e, t, n, r);
239
189
  }
240
190
  };
241
- return T;
191
+ return b;
242
192
  }
243
- function h(e, t) {
193
+ function m(e, t) {
244
194
  return Object.assign(e, { batch: t });
245
195
  }
246
196
  //#endregion
247
- export { c as assertBatchTables, m as buildAdapterOps, p as buildTxContext, h as withIndexedDbTransactions };
197
+ export { s as assertBatchTables, p as buildAdapterOps, f as buildTxContext, m as withIndexedDbTransactions };
248
198
 
249
199
  //# sourceMappingURL=adapter-core.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.js","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder, type NativeRange, type QueryContext } from './query';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n IndexedDbVaultStore,\n KeyOf,\n MetricsEvent,\n RecordOf,\n TransactionContext,\n VaultStore,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/** @internal Full backend protocol implemented by each adapter. A single flat interface — no StorageCore/StorageBackend split. */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n /** Returns live (non-expired) record count for the table. */\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: 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 /**\n * Optional: fetch all live primary keys without materialising full records.\n * When present, `keys()` uses this instead of `getAll()` + key extraction.\n * For tables with TTL, implementations may still fall back to `getAll()` to\n * filter expired keys accurately.\n */\n getAllKeys?<T extends K>(table: T): Promise<KeyOf<S, T>[]>;\n /**\n * Optional secondary-index range fetch. When present, `query()` activates `getIndexRange` in\n * the QueryContext so that a leading `equals`, `between`, or `startsWith` on an indexed field\n * uses an IDB index instead of a full-table scan.\n */\n getByIndexRange?<T extends K>(table: T, field: string, range: NativeRange): Promise<RecordOf<S, T>[]>;\n /**\n * Optional primary-key range fetch for push-down queries. When present, `query()` activates\n * `getRange` in the QueryContext so that a leading `equals`, `between`, or `startsWith` on the\n * key field avoids a full-table scan. Falls back to `getAll` when absent.\n */\n getByKeyRange?<T extends K>(table: T, range: NativeRange): Promise<RecordOf<S, T>[]>;\n /**\n * Optional: fetch multiple records by key in a single operation. Preserves key order; missing keys yield\n * `undefined`. Falls back to N individual `get` calls when absent.\n */\n getMany?<T extends K>(table: T, keys: KeyOf<S, T>[]): Promise<Array<RecordOf<S, T> | undefined>>;\n /** Raw record count including TTL-expired entries. Used only for debug(). */\n getRawCount?<T extends K>(table: T): Promise<number>;\n has<T extends K>(table: T, key: KeyOf<S, T>): Promise<boolean>;\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: 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\nconst getTimestamp: () => number = typeof performance !== 'undefined' ? () => performance.now() : () => Date.now();\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\nfunction getManyWithFallback<S extends AnySchema, K extends keyof S & string>(\n core: Pick<StorageBackend<S, K>, 'get' | 'getMany'>,\n table: K,\n keys: KeyOf<S, K>[],\n): Promise<Array<RecordOf<S, K> | undefined>> {\n if (core.getMany) return core.getMany(table, keys);\n\n return Promise.all(keys.map((k) => core.get(table, k)));\n}\n\nfunction buildQueryCtx<S extends AnySchema, K extends keyof S & string>(\n table: K,\n core: Pick<StorageBackend<S, K>, 'deleteMany' | 'getAll' | 'getByIndexRange' | 'getByKeyRange'>,\n schema: S,\n onMutate: (t: K) => void,\n): QueryContext<RecordOf<S, K>> {\n const entry = schema[table];\n const keyField = entry.key;\n const indexedFields = entry.indexes ? new Set(entry.indexes) : undefined;\n\n const deleteManyFn = async (records: RecordOf<S, K>[]): Promise<number> => {\n if (records.length === 0) return 0;\n\n const keys = records.map((r) => getRecordKey(schema, table, r));\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\n return deleted;\n };\n\n return {\n deleteMany: deleteManyFn,\n getIndexRange: core.getByIndexRange\n ? (field, range) => core.getByIndexRange!(table, field, range) as Promise<RecordOf<S, K>[]>\n : undefined,\n getRange: core.getByKeyRange\n ? (range) => core.getByKeyRange!(table, range) as Promise<RecordOf<S, K>[]>\n : undefined,\n indexedFields,\n keyField,\n source: () => core.getAll(table),\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): TransactionContext<S, K> {\n const applyValidation = validate ?? ((_: K, v: RecordOf<S, K>) => v);\n\n const checkScope = scope\n ? (t: K): void => {\n if (!scope.has(t)) {\n throw new VaultScopeError(`table \"${t}\" is not part of this batch scope`);\n }\n }\n : (_t: K): void => {};\n\n return {\n async clear(table) {\n checkScope(table);\n\n const live = await core.count(table);\n\n await core.clear(table);\n\n if (live > 0) onMutate(table);\n },\n async count(table) {\n checkScope(table);\n\n return core.count(table);\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\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\n return deleted;\n },\n async entries(table) {\n checkScope(table);\n\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.map((r) => [(r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>, r]);\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\n return getManyWithFallback(core, table, keys);\n },\n async getOrDefault(table, key, defaultFn, ttl) {\n checkScope(table);\n\n const existing = await core.get(table, key);\n\n if (existing !== undefined) return existing;\n\n const value = defaultFn();\n\n verifyKey(schema, table, key, value, 'getOrDefault: defaultFn()');\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return value;\n },\n async has(table, key) {\n checkScope(table);\n\n return core.has(table, key);\n },\n async isEmpty(table) {\n checkScope(table);\n\n return (await core.count(table)) === 0;\n },\n async keys(table, filter) {\n checkScope(table);\n\n if (filter) {\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.filter(filter).map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }\n\n // R1: prefer native getAllKeys when the backend supports it (e.g. IDB store.getAllKeys).\n if (core.getAllKeys) {\n return core.getAllKeys(table);\n }\n\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n checkScope(table);\n\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\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\n return createQueryBuilder(buildQueryCtx(table, core, schema, onMutate));\n },\n async update(table, key, changes, ttl) {\n checkScope(table);\n\n const current = await core.get(table, key);\n\n if (!current) return undefined;\n\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n\n verifyKey(schema, table, key, merged, 'update');\n await core.put(table, applyValidation(table, merged), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return merged;\n },\n async upsert(table, key, fn, ttl) {\n checkScope(table);\n\n const existing = await core.get(table, key);\n const value = fn(existing);\n\n verifyKey(schema, table, key, value, 'upsert: fn()');\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return value;\n },\n };\n}\n\n/* -------------------- buildAdapterOps -------------------- */\n\n/** Builds the portable surface first; IndexedDB opts into transactions at its factory boundary. */\nexport function buildAdapterOps<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: BaseAdapterOptions<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): VaultStore<S> {\n const { logger, onMetrics, validators } = options ?? {};\n\n const observers = createObserverHub<S>(\n (table) => core.getAll(table),\n logger\n ? (err) =>\n logger.error('[vault] observer notification failed', err instanceof Error ? err : new Error(String(err)))\n : undefined,\n );\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 // R6: Resolve optional backend capabilities once at construction — no per-call checks.\n const pruneAll = core.pruneAllExpired?.bind(core);\n const rawCountFn = core.getRawCount?.bind(core);\n\n const disconnectExternal = options?.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const validator = validators?.[table];\n\n if (!validator) return value;\n\n try {\n return validator.parse(value) as RecordOf<S, K>;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n\n /* Timed wrapper — only wraps when onMetrics is present, otherwise zero overhead. */\n const timed = <T>(table: string, op: MetricsEvent['operation'], fn: () => Promise<T>): Promise<T> => {\n if (!onMetrics) return fn();\n\n const start = getTimestamp();\n\n return fn().finally(() => onMetrics({ duration: getTimestamp() - start, operation: op, table }));\n };\n\n options?.onTransactions?.({ notifyMutation, validate });\n\n const txCtx = buildTxContext<S, keyof S & string>(schema, core, 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: VaultStore<S> = {\n async clear(table) {\n checkDisposed();\n await timed(table, 'clear', () => txCtx.clear(table));\n },\n\n async count(table) {\n checkDisposed();\n\n return timed(table, 'count', () => core.count(table));\n },\n\n async debug() {\n checkDisposed();\n\n const tableNames = Object.keys(schema) as Array<keyof S & string>;\n const entries = await Promise.all(\n tableNames.map(async (name) => {\n // getRawCount must be called before core.count() because count() has\n // lazy-eviction side effects (deletes expired entries from the store).\n const raw = rawCountFn ? await rawCountFn(name) : undefined;\n const live = await core.count(name);\n\n const expiredCount = raw !== undefined ? Math.max(0, raw - live) : 0;\n\n return { expiredCount, name, recordCount: live };\n }),\n );\n\n return { tables: entries } as DebugInfo<S>;\n },\n\n async delete(table, key) {\n checkDisposed();\n\n return timed(table, 'delete', () => txCtx.delete(table, key));\n // count cache invalidated by notifyMutation inside txCtx.delete\n },\n\n async deleteMany(table, keys) {\n checkDisposed();\n\n return timed(table, 'deleteMany', () => txCtx.deleteMany(table, keys));\n // count cache invalidated by notifyMutation inside txCtx.deleteMany\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 entries(table) {\n checkDisposed();\n\n return timed(table, 'entries', () => txCtx.entries(table));\n },\n\n async get(table, key) {\n checkDisposed();\n\n return timed(table, 'get', () => txCtx.get(table, key));\n },\n\n async getAll(table) {\n checkDisposed();\n\n return timed(table, 'getAll', () => txCtx.getAll(table));\n },\n\n async getMany(table, keys) {\n checkDisposed();\n\n return timed(table, 'getMany', () => txCtx.getMany(table, keys));\n },\n\n async getOrDefault(table, key, defaultFn, ttl) {\n checkDisposed();\n\n return timed(table, 'getOrDefault', () => txCtx.getOrDefault(table, key, defaultFn, ttl));\n },\n\n async has(table, key) {\n checkDisposed();\n\n return timed(table, 'has', () => txCtx.has(table, key));\n },\n\n async isEmpty(table) {\n checkDisposed();\n\n return timed(table, 'isEmpty', async () => (await core.count(table)) === 0);\n },\n\n async keys(table, filter) {\n checkDisposed();\n\n return timed(table, 'keys', () => txCtx.keys(table, filter));\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 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 return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async put(table, value, ttl) {\n checkDisposed();\n await timed(table, 'put', () => txCtx.put(table, value, ttl));\n // count cache invalidated via notifyMutation inside txCtx.put\n },\n\n async putAll(table, values, ttl) {\n checkDisposed();\n await timed(table, 'putAll', () => txCtx.putAll(table, values, ttl));\n // count cache invalidated via notifyMutation inside txCtx.putAll\n },\n\n query(table) {\n checkDisposed();\n\n const ctx = buildQueryCtx(table, core, schema, notifyMutation);\n\n if (onMetrics) {\n return createQueryBuilder({\n ...ctx,\n deleteMany: ctx.deleteMany\n ? (records) => timed(table, 'queryDelete', () => ctx.deleteMany!(records))\n : undefined,\n source: () => timed(table, 'query', () => core.getAll(table)),\n });\n }\n\n return createQueryBuilder(ctx);\n },\n\n async [Symbol.asyncDispose]() {\n await adapter.dispose();\n },\n\n async update(table, key, changes, ttl) {\n checkDisposed();\n\n return timed(table, 'update', () => txCtx.update(table, key, changes, ttl));\n },\n\n async upsert(table, key, fn, ttl) {\n checkDisposed();\n\n return timed(table, 'upsert', () => txCtx.upsert(table, key, fn, ttl));\n },\n };\n\n return adapter;\n}\n\n/** IndexedDB is the only backend that can bind this callback to one native transaction. */\nexport function withIndexedDbTransactions<S extends AnySchema>(\n store: VaultStore<S>,\n batch: BatchImpl<S>,\n): IndexedDbVaultStore<S> {\n return Object.assign(store, { batch }) as IndexedDbVaultStore<S>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"adapter-core.js","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder, type QueryContext } from './query';\nimport { assertPositiveFinite } from './ttl';\nimport type {\n AnySchema,\n BaseAdapterOptions,\n KeyOf,\n RecordOf,\n TransactionalVaultStore,\n TransactionContext,\n VaultStore,\n} from './types';\n\n/* -------------------- Internal backend protocol -------------------- */\n\n/** @internal Full backend protocol implemented by each adapter. A single flat interface — no StorageCore/StorageBackend split. */\nexport type StorageBackend<S extends AnySchema, K extends keyof S & string = keyof S & string> = {\n clear<T extends K>(table: T): Promise<void>;\n /** Returns live (non-expired) record count for the table. */\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: 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 /**\n * Optional: fetch all live primary keys without materialising full records.\n * When present, `keys()` uses this instead of `getAll()` + key extraction.\n * For tables with TTL, implementations may still fall back to `getAll()` to\n * filter expired keys accurately.\n */\n getAllKeys?<T extends K>(table: T): Promise<KeyOf<S, T>[]>;\n /**\n * Optional: fetch multiple records by key in a single operation. Preserves key order; missing keys yield\n * `undefined`. Falls back to N individual `get` calls when absent.\n */\n getMany?<T extends K>(table: T, keys: 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: 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: 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\nfunction getManyWithFallback<S extends AnySchema, K extends keyof S & string>(\n core: Pick<StorageBackend<S, K>, 'get' | 'getMany'>,\n table: K,\n keys: KeyOf<S, K>[],\n): Promise<Array<RecordOf<S, K> | undefined>> {\n if (core.getMany) return core.getMany(table, keys);\n\n return Promise.all(keys.map((k) => core.get(table, k)));\n}\n\nfunction buildQueryCtx<S extends AnySchema, K extends keyof S & string>(\n table: K,\n core: Pick<StorageBackend<S, K>, 'deleteMany' | 'getAll'>,\n schema: S,\n onMutate: (t: K) => void,\n): QueryContext<RecordOf<S, K>> {\n const deleteManyFn = async (records: RecordOf<S, K>[]): Promise<number> => {\n if (records.length === 0) return 0;\n\n const keys = records.map((r) => getRecordKey(schema, table, r));\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\n return deleted;\n };\n\n return {\n deleteMany: deleteManyFn,\n source: () => core.getAll(table),\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): TransactionContext<S, K> {\n const applyValidation = validate ?? ((_: K, v: RecordOf<S, K>) => v);\n\n const checkScope = scope\n ? (t: K): void => {\n if (!scope.has(t)) {\n throw new VaultScopeError(`table \"${t}\" is not part of this batch scope`);\n }\n }\n : (_t: K): void => {};\n\n return {\n async clear(table) {\n checkScope(table);\n\n const live = await core.count(table);\n\n await core.clear(table);\n\n if (live > 0) onMutate(table);\n },\n async count(table) {\n checkScope(table);\n\n return core.count(table);\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\n const deleted = await core.deleteMany(table, keys);\n\n if (deleted > 0) onMutate(table);\n\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\n return getManyWithFallback(core, table, keys);\n },\n async has(table, key) {\n checkScope(table);\n\n return core.has(table, key);\n },\n async isEmpty(table) {\n checkScope(table);\n\n return (await core.count(table)) === 0;\n },\n async keys(table, filter) {\n checkScope(table);\n\n if (filter) {\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.filter(filter).map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n }\n\n // R1: prefer native getAllKeys when the backend supports it (e.g. IDB store.getAllKeys).\n if (core.getAllKeys) {\n return core.getAllKeys(table);\n }\n\n const records = await core.getAll(table);\n const keyField = schema[table].key;\n\n return records.map((r) => (r as Record<string, unknown>)[keyField] as KeyOf<S, typeof table>);\n },\n async put(table, value, ttl) {\n checkScope(table);\n\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\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\n return createQueryBuilder(buildQueryCtx(table, core, schema, onMutate));\n },\n async update(table, key, changes, ttl) {\n checkScope(table);\n\n const current = await core.get(table, key);\n\n if (!current) return undefined;\n\n const merged = { ...current, ...changes } as RecordOf<S, typeof table>;\n\n verifyKey(schema, table, key, merged, 'update');\n await core.put(table, applyValidation(table, merged), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return merged;\n },\n async upsert(table, key, fn, ttl) {\n checkScope(table);\n\n const existing = await core.get(table, key);\n const value = fn(existing);\n\n verifyKey(schema, table, key, value, 'upsert: fn()');\n await core.put(table, applyValidation(table, value), resolveTtl(schema, table, ttl));\n onMutate(table);\n\n return value;\n },\n };\n}\n\n/* -------------------- buildAdapterOps -------------------- */\n\n/** Builds the portable surface first; IndexedDB opts into transactions at its factory boundary. */\nexport function buildAdapterOps<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: BaseAdapterOptions<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): VaultStore<S> {\n const { validators } = 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 // R6: Resolve optional backend capabilities once at construction — no per-call checks.\n const pruneAll = core.pruneAllExpired?.bind(core);\n\n const disconnectExternal = options?.onCrossTabMessage?.(notifyExternal) ?? undefined;\n\n const validate = <K extends keyof S & string>(table: K, value: RecordOf<S, K>): RecordOf<S, K> => {\n const validator = validators?.[table];\n\n if (!validator) return value;\n\n try {\n return validator.parse(value) as RecordOf<S, K>;\n } catch (err) {\n throw new VaultError(`validation failed for table \"${table}\"`, { cause: err });\n }\n };\n\n options?.onTransactions?.({ notifyMutation, validate });\n\n const txCtx = buildTxContext<S, keyof S & string>(schema, core, 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: VaultStore<S> = {\n async clear(table) {\n checkDisposed();\n await txCtx.clear(table);\n },\n\n async count(table) {\n checkDisposed();\n\n return core.count(table);\n },\n\n async delete(table, key) {\n checkDisposed();\n\n return txCtx.delete(table, key);\n },\n\n async deleteMany(table, keys) {\n checkDisposed();\n\n return txCtx.deleteMany(table, keys);\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 txCtx.get(table, key);\n },\n\n async getAll(table) {\n checkDisposed();\n\n return txCtx.getAll(table);\n },\n\n async getMany(table, keys) {\n checkDisposed();\n\n return txCtx.getMany(table, keys);\n },\n\n async has(table, key) {\n checkDisposed();\n\n return txCtx.has(table, key);\n },\n\n async isEmpty(table) {\n checkDisposed();\n\n return (await core.count(table)) === 0;\n },\n\n async keys(table, filter) {\n checkDisposed();\n\n return txCtx.keys(table, filter);\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 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 return Object.fromEntries(pairs) as { [K in keyof S & string]: number };\n },\n\n async put(table, value, ttl) {\n checkDisposed();\n await txCtx.put(table, value, ttl);\n },\n\n async putAll(table, values, ttl) {\n checkDisposed();\n await txCtx.putAll(table, values, ttl);\n },\n\n query(table) {\n checkDisposed();\n\n return createQueryBuilder(buildQueryCtx(table, core, schema, notifyMutation));\n },\n\n async [Symbol.asyncDispose]() {\n await adapter.dispose();\n },\n\n async update(table, key, changes, ttl) {\n checkDisposed();\n\n return txCtx.update(table, key, changes, ttl);\n },\n\n async upsert(table, key, fn, ttl) {\n checkDisposed();\n\n return txCtx.upsert(table, key, fn, ttl);\n },\n };\n\n return adapter;\n}\n\n/** IndexedDB is the only backend that can bind this callback to one native transaction. */\nexport function withIndexedDbTransactions<S extends AnySchema>(\n store: VaultStore<S>,\n batch: BatchImpl<S>,\n): TransactionalVaultStore<S> {\n return Object.assign(store, { batch }) as TransactionalVaultStore<S>;\n}\n"],"mappings":"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"}