@vielzeug/vault 1.0.4 → 2.0.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 (86) hide show
  1. package/README.md +18 -59
  2. package/dist/_dev.cjs +1 -1
  3. package/dist/_dev.cjs.map +1 -1
  4. package/dist/_dev.js +3 -8
  5. package/dist/_dev.js.map +1 -1
  6. package/dist/adapter-core.cjs +1 -1
  7. package/dist/adapter-core.cjs.map +1 -1
  8. package/dist/adapter-core.d.ts +6 -3
  9. package/dist/adapter-core.d.ts.map +1 -1
  10. package/dist/adapter-core.js +138 -148
  11. package/dist/adapter-core.js.map +1 -1
  12. package/dist/adapters/indexeddb.cjs +1 -1
  13. package/dist/adapters/indexeddb.cjs.map +1 -1
  14. package/dist/adapters/indexeddb.d.ts +2 -5
  15. package/dist/adapters/indexeddb.d.ts.map +1 -1
  16. package/dist/adapters/indexeddb.js +113 -127
  17. package/dist/adapters/indexeddb.js.map +1 -1
  18. package/dist/adapters/memory.cjs +1 -1
  19. package/dist/adapters/memory.cjs.map +1 -1
  20. package/dist/adapters/memory.d.ts +3 -9
  21. package/dist/adapters/memory.d.ts.map +1 -1
  22. package/dist/adapters/memory.js +104 -140
  23. package/dist/adapters/memory.js.map +1 -1
  24. package/dist/adapters/webstorage.cjs +1 -1
  25. package/dist/adapters/webstorage.cjs.map +1 -1
  26. package/dist/adapters/webstorage.d.ts +3 -3
  27. package/dist/adapters/webstorage.d.ts.map +1 -1
  28. package/dist/adapters/webstorage.js +72 -67
  29. package/dist/adapters/webstorage.js.map +1 -1
  30. package/dist/errors.cjs.map +1 -1
  31. package/dist/errors.js.map +1 -1
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.d.ts +2 -7
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +8 -10
  36. package/dist/internal.cjs +1 -1
  37. package/dist/internal.cjs.map +1 -1
  38. package/dist/internal.d.ts +8 -37
  39. package/dist/internal.d.ts.map +1 -1
  40. package/dist/internal.js +48 -98
  41. package/dist/internal.js.map +1 -1
  42. package/dist/migration.cjs +1 -1
  43. package/dist/migration.cjs.map +1 -1
  44. package/dist/migration.js +1 -3
  45. package/dist/migration.js.map +1 -1
  46. package/dist/prune.cjs +1 -1
  47. package/dist/prune.cjs.map +1 -1
  48. package/dist/prune.d.ts +2 -2
  49. package/dist/prune.d.ts.map +1 -1
  50. package/dist/prune.js +13 -13
  51. package/dist/prune.js.map +1 -1
  52. package/dist/query.cjs +1 -1
  53. package/dist/query.cjs.map +1 -1
  54. package/dist/query.d.ts +6 -6
  55. package/dist/query.d.ts.map +1 -1
  56. package/dist/query.js +1 -1
  57. package/dist/query.js.map +1 -1
  58. package/dist/streaming.d.ts +1 -21
  59. package/dist/streaming.d.ts.map +1 -1
  60. package/dist/ttl.cjs +1 -1
  61. package/dist/ttl.cjs.map +1 -1
  62. package/dist/ttl.d.ts +2 -47
  63. package/dist/ttl.d.ts.map +1 -1
  64. package/dist/ttl.js +1 -8
  65. package/dist/ttl.js.map +1 -1
  66. package/dist/types.cjs.map +1 -1
  67. package/dist/types.d.ts +29 -316
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/types.js.map +1 -1
  70. package/dist/vault.cjs +1 -1
  71. package/dist/vault.cjs.map +1 -1
  72. package/dist/vault.iife.js +1 -1
  73. package/dist/vault.iife.js.map +1 -1
  74. package/dist/vault.js +1 -1
  75. package/dist/vault.js.map +1 -1
  76. package/dist/versioned-codec.d.ts +1 -43
  77. package/dist/versioned-codec.d.ts.map +1 -1
  78. package/package.json +4 -4
  79. package/dist/streaming.cjs +0 -2
  80. package/dist/streaming.cjs.map +0 -1
  81. package/dist/streaming.js +0 -21
  82. package/dist/streaming.js.map +0 -1
  83. package/dist/versioned-codec.cjs +0 -2
  84. package/dist/versioned-codec.cjs.map +0 -1
  85. package/dist/versioned-codec.js +0 -32
  86. package/dist/versioned-codec.js.map +0 -1
package/README.md CHANGED
@@ -1,74 +1,33 @@
1
1
  # @vielzeug/vault
2
2
 
3
- > Typed browser storage with a compact API for LocalStorage, SessionStorage, IndexedDB, and Memory.
3
+ > Typed browser storage with portable string/number keys, TTL, queries, and snapshot observation.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@vielzeug/vault)](https://www.npmjs.com/package/@vielzeug/vault) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
-
7
- <details>
8
- <summary>Quick Reference</summary>
9
-
10
- **Package:** `@vielzeug/vault` &nbsp;·&nbsp; **Category:** Storage
11
-
12
- **Key exports:** `createLocalStorage`, `createSessionStorage`, `createIndexedDB`, `createMemory`, `table`, `ttl`, `scheduleExpiredPrune`
13
-
14
- **When to use:** Structured, queryable browser storage with TTL, reactivity, and TypeScript types.
15
-
16
- **Related:** [@vielzeug/courier](https://vielzeug.dev/courier/) · [@vielzeug/rune](https://vielzeug.dev/rune/) · [@vielzeug/ripple](https://vielzeug.dev/ripple/) · [@vielzeug/spell](https://vielzeug.dev/spell/) · [@vielzeug/arsenal](https://vielzeug.dev/arsenal/)
17
-
18
- </details>
19
-
20
- `@vielzeug/vault` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
21
-
22
- ## Installation
23
-
24
- ```sh
25
- pnpm add @vielzeug/vault
26
- npm install @vielzeug/vault
27
- yarn add @vielzeug/vault
28
- ```
29
-
30
- ## Quick Start
5
+ ## Quick start
31
6
 
32
7
  ```ts
33
- import { createIndexedDB, table, ttl } from '@vielzeug/vault';
34
-
35
- type User = { id: number; name: string; age: number };
36
-
37
- const schema = {
38
- users: table<User>('id'),
39
- };
40
-
41
- const db = createIndexedDB({ name: 'my-app', schema, version: 1 });
8
+ import { createLocalStorage, table } from '@vielzeug/vault';
42
9
 
43
- await db.putAll('users', [
44
- { id: 1, name: 'Alice', age: 30 },
45
- { id: 2, name: 'Bob', age: 25 },
46
- ]);
10
+ type User = { id: number; name: string };
11
+ const store = createLocalStorage({ name: 'app', schema: { users: table<User>('id') } });
47
12
 
48
- // TTL always use the ttl.* helpers
49
- await db.put('users', { id: 3, name: 'Carol', age: 28 }, ttl.hours(1));
13
+ await store.put('users', { id: 1, name: 'Ada' });
14
+ const stop = store.observe('users', (users) => console.log(users));
15
+ stop();
16
+ ```
50
17
 
51
- const first = await db.query('users').between('age', 18, 99).orderBy('name').first();
18
+ `createMemory`, `createLocalStorage`, and `createSessionStorage` return the portable `VaultStore<S>` API. `createIndexedDB` returns `IndexedDbVaultStore<S>`, adding cursor `iterate()` and atomic `batch()` transactions.
52
19
 
53
- // observe always fires immediately with the current snapshot, then on each change
54
- const stop = db.observe('users', (rows) => console.log(rows.length));
20
+ All adapters store a fixed `{ value, expiresAt? }` envelope. Number and string primary keys are tagged internally, so `1` and `'1'` are distinct everywhere.
55
21
 
56
- // stop from outside via AbortController
57
- const controller = new AbortController();
58
- for await (const users of db.watch('users', { signal: controller.signal })) {
59
- console.log(users.length);
60
- }
22
+ ## Major migration
61
23
 
62
- (void first, stop);
63
- ```
24
+ - Replace `Adapter` with `VaultStore`; replace `IndexedDbAdapter` with `IndexedDbVaultStore`.
25
+ - `batch()` is now IndexedDB-only. Use `createIndexedDB` where atomic work is required.
26
+ - Use `observe()` for reactivity. `watch`, `observeMany`, constructor `signals`, and `toReadableStream` were removed.
27
+ - Custom codecs, `defaultCodec`, and `createVersionedCodec` were removed; migrate existing data to the fixed envelope before upgrading.
64
28
 
65
29
  ## Documentation
66
30
 
67
31
  - [Overview](https://vielzeug.dev/vault/)
68
- - [Usage Guide](https://vielzeug.dev/vault/usage)
69
- - [API Reference](https://vielzeug.dev/vault/api)
70
- - [Examples](https://vielzeug.dev/vault/examples)
71
-
72
- ## License
73
-
74
- MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
32
+ - [Usage](https://vielzeug.dev/vault/usage)
33
+ - [API](https://vielzeug.dev/vault/api)
package/dist/_dev.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=!globalThis.__VAULT_PROD__;function t(t){e&&console.warn(`[@vielzeug/vault] ${t}`)}function n(t,...n){e&&console.error(`[@vielzeug/vault] ${t}`,...n)}exports.error=n,exports.warn=t;
1
+ function e(e){}function t(e,...t){}exports.error=t,exports.warn=e;
2
2
  //# sourceMappingURL=_dev.cjs.map
package/dist/_dev.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __VAULT_PROD__?: boolean }).__VAULT_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/vault] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/vault] ${msg}`, ...args);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAAA,IAAM,EAAQ,CAAE,WAA4C,eAG5D,SAAgB,EAAK,EAAmB,CAClC,GAAO,QAAQ,KAAK,qBAAqB,GAAK,CACpD,CAGA,SAAgB,EAAM,EAAa,GAAG,EAAuB,CACvD,GAAO,QAAQ,MAAM,qBAAqB,IAAO,GAAG,CAAI,CAC9D"}
1
+ {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __VAULT_PROD__?: boolean }).__VAULT_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/vault] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/vault] ${msg}`, ...args);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC,CAGA,SAAgB,EAAM,EAAa,GAAG,EAAuB,CAE7D"}
package/dist/_dev.js CHANGED
@@ -1,12 +1,7 @@
1
1
  //#region src/_dev.ts
2
- var e = !globalThis.__VAULT_PROD__;
3
- function t(t) {
4
- e && console.warn(`[@vielzeug/vault] ${t}`);
5
- }
6
- function n(t, ...n) {
7
- e && console.error(`[@vielzeug/vault] ${t}`, ...n);
8
- }
2
+ function e(e) {}
3
+ function t(e, ...t) {}
9
4
  //#endregion
10
- export { n as error, t as warn };
5
+ export { t as error, e as warn };
11
6
 
12
7
  //# sourceMappingURL=_dev.js.map
package/dist/_dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __VAULT_PROD__?: boolean }).__VAULT_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/vault] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/vault] ${msg}`, ...args);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAAA,IAAM,IAAQ,CAAE,WAA4C;AAG5D,SAAgB,EAAK,GAAmB;CACtC,AAAI,KAAO,QAAQ,KAAK,qBAAqB,GAAK;AACpD;AAGA,SAAgB,EAAM,GAAa,GAAG,GAAuB;CAC3D,AAAI,KAAO,QAAQ,MAAM,qBAAqB,KAAO,GAAG,CAAI;AAC9D"}
1
+ {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __VAULT_PROD__?: boolean }).__VAULT_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/vault] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/vault] ${msg}`, ...args);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC;AAGA,SAAgB,EAAM,GAAa,GAAG,GAAuB,CAE7D"}
@@ -1,2 +1,2 @@
1
- const e=require("./_dev.cjs"),t=require("./errors.cjs"),n=require("./internal.cjs"),r=require("./query.cjs"),i=require("./ttl.cjs");var a=typeof performance<`u`?()=>performance.now():()=>Date.now();function o(e,t,n){return n!==void 0&&i.assertTtlMs(n,`put/putAll`),n??e[t].defaultTtl}function s(e,r,i,a,o){let s=n.getRecordKey(e,r,a);if(s!==i)throw new t.VaultError(`${o}: key field "${e[r].key}" must be "${String(i)}" but got "${String(s)}" in table "${r}"`)}function c(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(e,n,i,a,u){let d=a??((e,t)=>t),f=u?e=>{if(!u.has(e))throw new t.VaultScopeError(`table "${e}" is not part of this batch scope`)}:e=>{};return{async clear(e){f(e),await n.count(e)!==0&&(await n.clear(e),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(t){f(t);let r=await n.getAll(t),i=e[t].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(t,r,a,c){f(t);let l=await n.get(t,r);if(l!==void 0)return l;let u=a();return s(e,t,r,u,`getOrDefault: defaultFn()`),await n.put(t,d(t,u),o(e,t,c)),i(t),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(t,r){if(f(t),r){let i=await n.getAll(t),a=e[t].key;return i.filter(r).map(e=>e[a])}if(n.getAllKeys)return n.getAllKeys(t);let i=await n.getAll(t),a=e[t].key;return i.map(e=>e[a])},async put(t,r,a){f(t),await n.put(t,d(t,r),o(e,t,a)),i(t)},async putAll(t,r,a){if(f(t),r.length===0)return;let s=r.map(e=>d(t,e));await n.putAll(t,s,o(e,t,a)),i(t)},query(t){return f(t),r.createQueryBuilder(l(t,n,e,i))},async update(t,r,a,c){f(t);let l=await n.get(t,r);if(!l)return;let u={...l,...a};return s(e,t,r,u,`update`),await n.put(t,d(t,u),o(e,t,c)),i(t),u},async upsert(t,r,a,c){f(t);let l=a(await n.get(t,r));return s(e,t,r,l,`upsert: fn()`),await n.put(t,d(t,l),o(e,t,c)),i(t),l}}}function d(i,o,s){let{logger:c,onMetrics:d,signals:f,validators:p}=s??{},m=n.createObserverHub(e=>o.getAll(e),c?e=>c.error(e instanceof Error?e:Error(String(e)),`[vault] observer notification failed`):void 0),h=new Map,g=async e=>{if(h.has(e))return h.get(e);let t=await o.count(e);return h.set(e,t),t},_=e=>{h.delete(e),m.notify(e),s?.onMutation?.(e)},v=e=>{h.delete(e),m.notify(e)},y=o.pruneAllExpired?.bind(o),b=o.getRawCount?.bind(o),x=s?.onCrossTabMessage?.(v)??void 0;if(f)for(let[e,t]of Object.entries(f))t&&m.observe(e,e=>{t.update(()=>e)});let S=(e,n)=>{let r=p?.[e];if(!r)return n;try{return r.parse(n)}catch(n){throw new t.VaultError(`validation failed for table "${e}"`,{cause:n})}},C=(e,t,n)=>{if(!d)return n();let r=a();return n().finally(()=>d({duration:a()-r,operation:t,table:e}))},w=async(e,t)=>{let n=new Set,r=await e(u(i,o,e=>n.add(e),S,t));for(let e of n)_(e);return r},T=s?.buildBatch?.({notifyMutation:_,validate:S}),E=!1,D=u(i,o,_,S),O=n.createObserveMany(m),k=new AbortController,A=!1,j=()=>{if(A)throw new t.VaultDisposedError},M={async batch(n,r){if(j(),n.length===0)throw new t.VaultScopeError(`batch requires at least one table`);return!T&&!E&&(E=!0,e.warn(`batch() on this adapter is not atomic — concurrent mutations may interleave. Use createIndexedDB for atomic batches.`)),C(`*`,`batch`,()=>T?T(n,r):w(r,new Set(n)))},async clear(e){j(),await C(e,`clear`,()=>D.clear(e)),h.set(e,0)},async count(e){return j(),C(e,`count`,()=>g(e))},async debug(){j();let e=Object.keys(i);return{tables:await Promise.all(e.map(async e=>{let t=b?await b(e):void 0,n=await o.count(e);return h.set(e,n),{expiredCount:t===void 0?0:Math.max(0,t-n),name:e,recordCount:n}}))}},async delete(e,t){return j(),C(e,`delete`,()=>D.delete(e,t))},async deleteMany(e,t){return j(),C(e,`deleteMany`,()=>D.deleteMany(e,t))},get disposalSignal(){return k.signal},async dispose(){A||(A=!0,k.abort(),x?.(),m.dispose(),await o.dispose?.())},get disposed(){return A},async entries(e){return j(),C(e,`entries`,()=>D.entries(e))},async get(e,t){return j(),C(e,`get`,()=>D.get(e,t))},async getAll(e){return j(),C(e,`getAll`,()=>D.getAll(e))},async getMany(e,t){return j(),C(e,`getMany`,()=>D.getMany(e,t))},async getOrDefault(e,t,n,r){return j(),C(e,`getOrDefault`,()=>D.getOrDefault(e,t,n,r))},async has(e,t){return j(),C(e,`has`,()=>D.has(e,t))},async isEmpty(e){return j(),C(e,`isEmpty`,async()=>await g(e)===0)},async keys(e,t){return j(),C(e,`keys`,()=>D.keys(e,t))},observe(e,t,n){return j(),m.observe(e,t,n)},observeMany:(e,t,n)=>(j(),O(e,t,n)),async pruneExpired(e){j();let t=e??Object.keys(i);if(!e&&y){let e=await y();for(let[t,n]of Object.entries(e))n>0&&h.delete(t);return e}let n=await Promise.all(t.map(async e=>{let t=await o.pruneExpiredInTable(e);return t>0&&h.delete(e),[e,t]}));return Object.fromEntries(n)},async put(e,t,n){j(),await C(e,`put`,()=>D.put(e,t,n))},async putAll(e,t,n){j(),await C(e,`putAll`,()=>D.putAll(e,t,n))},query(e){j();let t=l(e,o,i,_);return d?r.createQueryBuilder({...t,deleteMany:t.deleteMany?n=>C(e,`queryDelete`,()=>t.deleteMany(n)):void 0,source:()=>C(e,`query`,()=>o.getAll(e))}):r.createQueryBuilder(t)},async[Symbol.asyncDispose](){await M.dispose()},async update(e,t,n,r){return j(),C(e,`update`,()=>D.update(e,t,n,r))},async upsert(e,t,n,r){return j(),C(e,`upsert`,()=>D.upsert(e,t,n,r))},watch(e,t){return j(),n.createWatchIterable(t=>m.observe(e,t),t?.mode??`latest`,t?.signal)}};return M}exports.buildAdapterOps=d,exports.buildTxContext=u;
1
+ const e=require("./errors.cjs"),t=require("./internal.cjs"),n=require("./query.cjs"),r=require("./ttl.cjs");var i=typeof performance<`u`?()=>performance.now():()=>Date.now();function a(e,t,n){return n!==void 0&&r.assertTtlMs(n,`put/putAll`),n??e[t].defaultTtl}function o(n,r,i,a,o){let s=t.getRecordKey(n,r,a);if(s!==i)throw new e.VaultError(`${o}: key field "${n[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,n,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(n=>t.getRecordKey(r,e,n)),s=await n.deleteMany(e,o);return s>0&&i(e),s},getIndexRange:n.getByIndexRange?(t,r)=>n.getByIndexRange(e,t,r):void 0,getRange:n.getByKeyRange?t=>n.getByKeyRange(e,t):void 0,indexedFields:s,keyField:o,source:()=>n.getAll(e)}}function l(t,r,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 r.count(e);await r.clear(e),t>0&&i(e)},async count(e){return f(e),r.count(e)},async delete(e,t){f(e);let n=await r.delete(e,t);return n&&i(e),n},async deleteMany(e,t){f(e);let n=await r.deleteMany(e,t);return n>0&&i(e),n},async entries(e){f(e);let n=await r.getAll(e),i=t[e].key;return n.map(e=>[e[i],e])},async get(e,t){return f(e),r.get(e,t)},async getAll(e){return f(e),r.getAll(e)},async getMany(e,t){return f(e),s(r,e,t)},async getOrDefault(e,n,s,c){f(e);let l=await r.get(e,n);if(l!==void 0)return l;let u=s();return o(t,e,n,u,`getOrDefault: defaultFn()`),await r.put(e,d(e,u),a(t,e,c)),i(e),u},async has(e,t){return f(e),r.has(e,t)},async isEmpty(e){return f(e),await r.count(e)===0},async keys(e,n){if(f(e),n){let i=await r.getAll(e),a=t[e].key;return i.filter(n).map(e=>e[a])}if(r.getAllKeys)return r.getAllKeys(e);let i=await r.getAll(e),a=t[e].key;return i.map(e=>e[a])},async put(e,n,o){f(e),await r.put(e,d(e,n),a(t,e,o)),i(e)},async putAll(e,n,o){if(f(e),n.length===0)return;let s=n.map(t=>d(e,t));await r.putAll(e,s,a(t,e,o)),i(e)},query(e){return f(e),n.createQueryBuilder(c(e,r,t,i))},async update(e,n,s,c){f(e);let l=await r.get(e,n);if(!l)return;let u={...l,...s};return o(t,e,n,u,`update`),await r.put(e,d(e,u),a(t,e,c)),i(e),u},async upsert(e,n,s,c){f(e);let l=s(await r.get(e,n));return o(t,e,n,l,`upsert: fn()`),await r.put(e,d(e,l),a(t,e,c)),i(e),l}}}function u(r,a,o){let{logger:s,onMetrics:u,validators:d}=o??{},f=t.createObserverHub(e=>a.getAll(e),s?e=>s.error(e instanceof Error?e:Error(String(e)),`[vault] observer notification failed`):void 0),p=new Map,m=async e=>{if(p.has(e))return p.get(e);let t=await a.count(e);return p.set(e,t),t},h=e=>{p.delete(e),f.notify(e),o?.onMutation?.(e)},g=e=>{p.delete(e),f.notify(e)},_=a.pruneAllExpired?.bind(a),v=a.getRawCount?.bind(a),y=o?.onCrossTabMessage?.(g)??void 0,b=(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})}},x=(e,t,n)=>{if(!u)return n();let r=i();return n().finally(()=>u({duration:i()-r,operation:t,table:e}))};o?.onTransactions?.({notifyMutation:h,validate:b});let S=l(r,a,h,b),C=new AbortController,w=!1,T=()=>{if(w)throw new e.VaultDisposedError},E={async clear(e){T(),await x(e,`clear`,()=>S.clear(e)),p.set(e,0)},async count(e){return T(),x(e,`count`,()=>m(e))},async debug(){T();let e=Object.keys(r);return{tables:await Promise.all(e.map(async e=>{let t=v?await v(e):void 0,n=await a.count(e);return p.set(e,n),{expiredCount:t===void 0?0:Math.max(0,t-n),name:e,recordCount:n}}))}},async delete(e,t){return T(),x(e,`delete`,()=>S.delete(e,t))},async deleteMany(e,t){return T(),x(e,`deleteMany`,()=>S.deleteMany(e,t))},get disposalSignal(){return C.signal},async dispose(){w||(w=!0,C.abort(),y?.(),f.dispose(),await a.dispose?.())},get disposed(){return w},async entries(e){return T(),x(e,`entries`,()=>S.entries(e))},async get(e,t){return T(),x(e,`get`,()=>S.get(e,t))},async getAll(e){return T(),x(e,`getAll`,()=>S.getAll(e))},async getMany(e,t){return T(),x(e,`getMany`,()=>S.getMany(e,t))},async getOrDefault(e,t,n,r){return T(),x(e,`getOrDefault`,()=>S.getOrDefault(e,t,n,r))},async has(e,t){return T(),x(e,`has`,()=>S.has(e,t))},async isEmpty(e){return T(),x(e,`isEmpty`,async()=>await m(e)===0)},async keys(e,t){return T(),x(e,`keys`,()=>S.keys(e,t))},observe(e,t,n){return T(),f.observe(e,t,n)},async pruneExpired(e){T();let t=e??Object.keys(r);if(!e&&_){let e=await _();for(let[t,n]of Object.entries(e))n>0&&p.delete(t);return e}let n=await Promise.all(t.map(async e=>{let t=await a.pruneExpiredInTable(e);return t>0&&p.delete(e),[e,t]}));return Object.fromEntries(n)},async put(e,t,n){T(),await x(e,`put`,()=>S.put(e,t,n))},async putAll(e,t,n){T(),await x(e,`putAll`,()=>S.putAll(e,t,n))},query(e){T();let t=c(e,a,r,h);return u?n.createQueryBuilder({...t,deleteMany:t.deleteMany?n=>x(e,`queryDelete`,()=>t.deleteMany(n)):void 0,source:()=>x(e,`query`,()=>a.getAll(e))}):n.createQueryBuilder(t)},async[Symbol.asyncDispose](){await E.dispose()},async update(e,t,n,r){return T(),x(e,`update`,()=>S.update(e,t,n,r))},async upsert(e,t,n,r){return T(),x(e,`upsert`,()=>S.upsert(e,t,n,r))}};return E}function d(e,t){return Object.assign(e,{batch:t})}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 type {\n Adapter,\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n KeyOf,\n MetricsEvent,\n RecordOf,\n TransactionContext,\n TtlMs,\n} from './types';\n\nimport { warn } from './_dev';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserveMany, createObserverHub, createWatchIterable, getRecordKey } from './internal';\nimport { createQueryBuilder, type NativeRange, type QueryContext } from './query';\nimport { assertTtlMs } from './ttl';\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?: TtlMs): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: TtlMs): 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\nfunction resolveTtl<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n ttl?: TtlMs,\n): TtlMs | undefined {\n if (ttl !== undefined) assertTtlMs(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 if (live === 0) return;\n\n await core.clear(table);\n 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\nexport function buildAdapterOps<S extends AnySchema>(\n schema: S,\n core: StorageBackend<S>,\n options?: BaseAdapterOptions<S> & {\n buildBatch?: (deps: BatchDeps<S>) => BatchImpl<S>;\n onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;\n onMutation?: (table: keyof S & string) => void;\n },\n): Adapter<S> {\n const { logger, onMetrics, signals, validators } = options ?? {};\n\n const observers = createObserverHub<S>(\n (table) => core.getAll(table),\n logger\n ? (err) =>\n logger.error(err instanceof Error ? err : new Error(String(err)), '[vault] observer notification failed')\n : undefined,\n );\n\n // Per-table live-count cache. Populated lazily on first count() call.\n // Invalidated on every mutation via notifyMutation.\n const countCache = new Map<string, number>();\n\n const getCachedCount = async (table: keyof S & string): Promise<number> => {\n if (countCache.has(table)) return countCache.get(table)!;\n\n const n = await core.count(table);\n\n countCache.set(table, n);\n\n return n;\n };\n\n const notifyMutation = (table: keyof S & string): void => {\n countCache.delete(table); // invalidate on any mutation\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 need to invalidate the count cache and notify observers.\n const notifyExternal = (table: keyof S & string): void => {\n countCache.delete(table);\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 if (signals) {\n for (const [tableName, signal] of Object.entries(signals)) {\n if (signal) {\n observers.observe(tableName as keyof S & string, (records) => {\n signal.update(() => records as RecordOf<S, keyof S>[]);\n });\n }\n }\n }\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 const deferredBatch = async <K extends keyof S & string, R>(\n fn: (tx: TransactionContext<S, K>) => Promise<R>,\n scope: ReadonlySet<string>,\n ): Promise<R> => {\n const dirty = new Set<K>();\n const tx = buildTxContext<S, K>(schema, core as StorageBackend<S, K>, (t) => dirty.add(t), validate, scope);\n const result = await fn(tx);\n\n for (const t of dirty) {\n notifyMutation(t);\n }\n\n return result;\n };\n\n const nativeBatch = options?.buildBatch?.({ notifyMutation, validate });\n\n let warnedNonAtomic = false;\n\n const txCtx = buildTxContext<S, keyof S & string>(schema, core, notifyMutation, validate);\n\n const observeMany = createObserveMany<S>(observers);\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: Adapter<S> = {\n async batch<K extends keyof S & string, R>(tables: readonly K[], fn: (tx: TransactionContext<S, K>) => Promise<R>) {\n checkDisposed();\n\n if (tables.length === 0) throw new VaultScopeError('batch requires at least one table');\n\n if (!nativeBatch && !warnedNonAtomic) {\n warnedNonAtomic = true;\n warn(\n 'batch() on this adapter is not atomic — concurrent mutations may interleave. Use createIndexedDB for atomic batches.',\n );\n }\n\n return timed('*', 'batch', () => (nativeBatch ? nativeBatch(tables, fn) : deferredBatch(fn, new Set(tables))));\n },\n\n async clear(table) {\n checkDisposed();\n await timed(table, 'clear', () => txCtx.clear(table));\n countCache.set(table, 0); // we know count is 0 after clear\n },\n\n async count(table) {\n checkDisposed();\n\n return timed(table, 'count', () => getCachedCount(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 // Warm the count cache so subsequent adapter.count() calls don't re-hit the backend.\n countCache.set(name, live);\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 getCachedCount(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 observeMany: (tables, listener, opts) => {\n checkDisposed();\n\n return observeMany(tables, listener, opts);\n },\n\n async pruneExpired(tables?: readonly (keyof S & string)[]) {\n checkDisposed();\n\n const tableNames = tables ?? (Object.keys(schema) as Array<keyof S & string>);\n\n if (!tables && pruneAll) {\n const result = await pruneAll();\n\n for (const [name, count] of Object.entries(result)) {\n if (count > 0) countCache.delete(name);\n }\n\n return result as { [K in keyof S & string]: number };\n }\n\n const pairs = await Promise.all(\n tableNames.map(async (name) => {\n const pruned = await core.pruneExpiredInTable(name);\n\n if (pruned > 0) countCache.delete(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 watch(table, options) {\n checkDisposed();\n\n return createWatchIterable<RecordOf<S, typeof table>>(\n (listener) => observers.observe(table, listener),\n options?.mode ?? 'latest',\n options?.signal,\n );\n },\n };\n\n return adapter;\n}\n"],"mappings":"oIAiFA,IAAM,EAA6B,OAAO,YAAgB,QAAoB,YAAY,IAAI,MAAU,KAAK,IAAI,EAEjH,SAAS,EACP,EACA,EACA,EACmB,CAGnB,OAFI,IAAQ,IAAA,IAAW,EAAA,YAAY,EAAK,YAAY,EAE7C,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,CAC9B,IAAM,EAAQ,EAAO,GACf,EAAW,EAAM,IACjB,EAAgB,EAAM,QAAU,IAAI,IAAI,EAAM,OAAO,EAAI,IAAA,GAa/D,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,cAAe,EAAK,iBACf,EAAO,IAAU,EAAK,gBAAiB,EAAO,EAAO,CAAK,EAC3D,IAAA,GACJ,SAAU,EAAK,cACV,GAAU,EAAK,cAAe,EAAO,CAAK,EAC3C,IAAA,GACJ,gBACA,WACA,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,EAIZ,MAFe,EAAK,MAAM,CAAK,IAEtB,IAEb,MAAM,EAAK,MAAM,CAAK,EACtB,EAAS,CAAK,EAChB,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,QAAQ,EAAO,CACnB,EAAW,CAAK,EAEhB,IAAM,EAAU,MAAM,EAAK,OAAO,CAAK,EACjC,EAAW,EAAO,EAAM,CAAC,IAE/B,OAAO,EAAQ,IAAK,GAAM,CAAE,EAA8B,GAAqC,CAAC,CAAC,CACnG,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,aAAa,EAAO,EAAK,EAAW,EAAK,CAC7C,EAAW,CAAK,EAEhB,IAAM,EAAW,MAAM,EAAK,IAAI,EAAO,CAAG,EAE1C,GAAI,IAAa,IAAA,GAAW,OAAO,EAEnC,IAAM,EAAQ,EAAU,EAMxB,OAJA,EAAU,EAAQ,EAAO,EAAK,EAAO,2BAA2B,EAChE,MAAM,EAAK,IAAI,EAAO,EAAgB,EAAO,CAAK,EAAG,EAAW,EAAQ,EAAO,CAAG,CAAC,EACnF,EAAS,CAAK,EAEP,CACT,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,CAIA,SAAgB,EACd,EACA,EACA,EAKY,CACZ,GAAM,CAAE,SAAQ,YAAW,UAAS,cAAe,GAAW,CAAC,EAEzD,EAAY,EAAA,kBACf,GAAU,EAAK,OAAO,CAAK,EAC5B,EACK,GACC,EAAO,MAAM,aAAe,MAAQ,EAAU,MAAM,OAAO,CAAG,CAAC,EAAG,sCAAsC,EAC1G,IAAA,EACN,EAIM,EAAa,IAAI,IAEjB,EAAiB,KAAO,IAA6C,CACzE,GAAI,EAAW,IAAI,CAAK,EAAG,OAAO,EAAW,IAAI,CAAK,EAEtD,IAAM,EAAI,MAAM,EAAK,MAAM,CAAK,EAIhC,OAFA,EAAW,IAAI,EAAO,CAAC,EAEhB,CACT,EAEM,EAAkB,GAAkC,CACxD,EAAW,OAAO,CAAK,EACvB,EAAU,OAAO,CAAK,EACtB,GAAS,aAAa,CAAK,CAC7B,EAIM,EAAkB,GAAkC,CACxD,EAAW,OAAO,CAAK,EACvB,EAAU,OAAO,CAAK,CACxB,EAGM,EAAW,EAAK,iBAAiB,KAAK,CAAI,EAC1C,EAAa,EAAK,aAAa,KAAK,CAAI,EAExC,EAAqB,GAAS,oBAAoB,CAAc,GAAK,IAAA,GAE3E,GAAI,MACG,GAAM,CAAC,EAAW,KAAW,OAAO,QAAQ,CAAO,EAClD,GACF,EAAU,QAAQ,EAAgC,GAAY,CAC5D,EAAO,WAAa,CAAiC,CACvD,CAAC,EAKP,IAAM,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,EAGM,GAAY,EAAe,EAA+B,IAAqC,CACnG,GAAI,CAAC,EAAW,OAAO,EAAG,EAE1B,IAAM,EAAQ,EAAa,EAE3B,OAAO,EAAG,CAAC,CAAC,YAAc,EAAU,CAAE,SAAU,EAAa,EAAI,EAAO,UAAW,EAAI,OAAM,CAAC,CAAC,CACjG,EAEM,EAAgB,MACpB,EACA,IACe,CACf,IAAM,EAAQ,IAAI,IAEZ,EAAS,MAAM,EADV,EAAqB,EAAQ,EAA+B,GAAM,EAAM,IAAI,CAAC,EAAG,EAAU,CAC7E,CAAE,EAE1B,IAAK,IAAM,KAAK,EACd,EAAe,CAAC,EAGlB,OAAO,CACT,EAEM,EAAc,GAAS,aAAa,CAAE,iBAAgB,UAAS,CAAC,EAElE,EAAkB,GAEhB,EAAQ,EAAoC,EAAQ,EAAM,EAAgB,CAAQ,EAElF,EAAc,EAAA,kBAAqB,CAAS,EAE5C,EAAoB,IAAI,gBAC1B,EAAW,GAET,MAA4B,CAChC,GAAI,EAAU,MAAM,IAAI,EAAA,kBAC1B,EAEM,EAAsB,CAC1B,MAAM,MAAqC,EAAsB,EAAkD,CAGjH,GAFA,EAAc,EAEV,EAAO,SAAW,EAAG,MAAM,IAAI,EAAA,gBAAgB,mCAAmC,EAStF,MAPI,CAAC,GAAe,CAAC,IACnB,EAAkB,GAClB,EAAA,KACE,sHACF,GAGK,EAAM,IAAK,YAAgB,EAAc,EAAY,EAAQ,CAAE,EAAI,EAAc,EAAI,IAAI,IAAI,CAAM,CAAC,CAAE,CAC/G,EAEA,MAAM,MAAM,EAAO,CACjB,EAAc,EACd,MAAM,EAAM,EAAO,YAAe,EAAM,MAAM,CAAK,CAAC,EACpD,EAAW,IAAI,EAAO,CAAC,CACzB,EAEA,MAAM,MAAM,EAAO,CAGjB,OAFA,EAAc,EAEP,EAAM,EAAO,YAAe,EAAe,CAAK,CAAC,CAC1D,EAEA,MAAM,OAAQ,CACZ,EAAc,EAEd,IAAM,EAAa,OAAO,KAAK,CAAM,EAiBrC,MAAO,CAAE,OAAQ,MAhBK,QAAQ,IAC5B,EAAW,IAAI,KAAO,IAAS,CAG7B,IAAM,EAAM,EAAa,MAAM,EAAW,CAAI,EAAI,IAAA,GAC5C,EAAO,MAAM,EAAK,MAAM,CAAI,EAOlC,OAJA,EAAW,IAAI,EAAM,CAAI,EAIlB,CAAE,aAFY,IAAQ,IAAA,GAAsC,EAA1B,KAAK,IAAI,EAAG,EAAM,CAAI,EAExC,OAAM,YAAa,CAAK,CACjD,CAAC,CACH,CAEyB,CAC3B,EAEA,MAAM,OAAO,EAAO,EAAK,CAGvB,OAFA,EAAc,EAEP,EAAM,EAAO,aAAgB,EAAM,OAAO,EAAO,CAAG,CAAC,CAE9D,EAEA,MAAM,WAAW,EAAO,EAAM,CAG5B,OAFA,EAAc,EAEP,EAAM,EAAO,iBAAoB,EAAM,WAAW,EAAO,CAAI,CAAC,CAEvE,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,QAAQ,EAAO,CAGnB,OAFA,EAAc,EAEP,EAAM,EAAO,cAAiB,EAAM,QAAQ,CAAK,CAAC,CAC3D,EAEA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAc,EAEP,EAAM,EAAO,UAAa,EAAM,IAAI,EAAO,CAAG,CAAC,CACxD,EAEA,MAAM,OAAO,EAAO,CAGlB,OAFA,EAAc,EAEP,EAAM,EAAO,aAAgB,EAAM,OAAO,CAAK,CAAC,CACzD,EAEA,MAAM,QAAQ,EAAO,EAAM,CAGzB,OAFA,EAAc,EAEP,EAAM,EAAO,cAAiB,EAAM,QAAQ,EAAO,CAAI,CAAC,CACjE,EAEA,MAAM,aAAa,EAAO,EAAK,EAAW,EAAK,CAG7C,OAFA,EAAc,EAEP,EAAM,EAAO,mBAAsB,EAAM,aAAa,EAAO,EAAK,EAAW,CAAG,CAAC,CAC1F,EAEA,MAAM,IAAI,EAAO,EAAK,CAGpB,OAFA,EAAc,EAEP,EAAM,EAAO,UAAa,EAAM,IAAI,EAAO,CAAG,CAAC,CACxD,EAEA,MAAM,QAAQ,EAAO,CAGnB,OAFA,EAAc,EAEP,EAAM,EAAO,UAAW,SAAa,MAAM,EAAe,CAAK,IAAO,CAAC,CAChF,EAEA,MAAM,KAAK,EAAO,EAAQ,CAGxB,OAFA,EAAc,EAEP,EAAM,EAAO,WAAc,EAAM,KAAK,EAAO,CAAM,CAAC,CAC7D,EAEA,QAAQ,EAAO,EAAU,EAAM,CAG7B,OAFA,EAAc,EAEP,EAAU,QAAQ,EAAO,EAAU,CAAI,CAChD,EAEA,aAAc,EAAQ,EAAU,KAC9B,EAAc,EAEP,EAAY,EAAQ,EAAU,CAAI,GAG3C,MAAM,aAAa,EAAwC,CACzD,EAAc,EAEd,IAAM,EAAa,GAAW,OAAO,KAAK,CAAM,EAEhD,GAAI,CAAC,GAAU,EAAU,CACvB,IAAM,EAAS,MAAM,EAAS,EAE9B,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAC3C,EAAQ,GAAG,EAAW,OAAO,CAAI,EAGvC,OAAO,CACT,CAEA,IAAM,EAAQ,MAAM,QAAQ,IAC1B,EAAW,IAAI,KAAO,IAAS,CAC7B,IAAM,EAAS,MAAM,EAAK,oBAAoB,CAAI,EAIlD,OAFI,EAAS,GAAG,EAAW,OAAO,CAAI,EAE/B,CAAC,EAAM,CAAM,CACtB,CAAC,CACH,EAEA,OAAO,OAAO,YAAY,CAAK,CACjC,EAEA,MAAM,IAAI,EAAO,EAAO,EAAK,CAC3B,EAAc,EACd,MAAM,EAAM,EAAO,UAAa,EAAM,IAAI,EAAO,EAAO,CAAG,CAAC,CAE9D,EAEA,MAAM,OAAO,EAAO,EAAQ,EAAK,CAC/B,EAAc,EACd,MAAM,EAAM,EAAO,aAAgB,EAAM,OAAO,EAAO,EAAQ,CAAG,CAAC,CAErE,EAEA,MAAM,EAAO,CACX,EAAc,EAEd,IAAM,EAAM,EAAc,EAAO,EAAM,EAAQ,CAAc,EAY7D,OAVI,EACK,EAAA,mBAAmB,CACxB,GAAG,EACH,WAAY,EAAI,WACX,GAAY,EAAM,EAAO,kBAAqB,EAAI,WAAY,CAAO,CAAC,EACvE,IAAA,GACJ,WAAc,EAAM,EAAO,YAAe,EAAK,OAAO,CAAK,CAAC,CAC9D,CAAC,EAGI,EAAA,mBAAmB,CAAG,CAC/B,EAEA,MAAO,OAAO,eAAgB,CAC5B,MAAM,EAAQ,QAAQ,CACxB,EAEA,MAAM,OAAO,EAAO,EAAK,EAAS,EAAK,CAGrC,OAFA,EAAc,EAEP,EAAM,EAAO,aAAgB,EAAM,OAAO,EAAO,EAAK,EAAS,CAAG,CAAC,CAC5E,EAEA,MAAM,OAAO,EAAO,EAAK,EAAI,EAAK,CAGhC,OAFA,EAAc,EAEP,EAAM,EAAO,aAAgB,EAAM,OAAO,EAAO,EAAK,EAAI,CAAG,CAAC,CACvE,EAEA,MAAM,EAAO,EAAS,CAGpB,OAFA,EAAc,EAEP,EAAA,oBACJ,GAAa,EAAU,QAAQ,EAAO,CAAQ,EAC/C,GAAS,MAAQ,SACjB,GAAS,MACX,CACF,CACF,EAEA,OAAO,CACT"}
1
+ {"version":3,"file":"adapter-core.cjs","names":[],"sources":["../src/adapter-core.ts"],"sourcesContent":["import type {\n AnySchema,\n BaseAdapterOptions,\n DebugInfo,\n IndexedDbVaultStore,\n KeyOf,\n MetricsEvent,\n RecordOf,\n TransactionContext,\n TtlMs,\n VaultStore,\n} from './types';\n\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\nimport { createObserverHub, getRecordKey } from './internal';\nimport { createQueryBuilder, type NativeRange, type QueryContext } from './query';\nimport { assertTtlMs } from './ttl';\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?: TtlMs): Promise<void>;\n putAll<T extends K>(table: T, values: RecordOf<S, T>[], ttl?: TtlMs): 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\nfunction resolveTtl<S extends AnySchema, K extends keyof S & string>(\n schema: S,\n table: K,\n ttl?: TtlMs,\n): TtlMs | undefined {\n if (ttl !== undefined) assertTtlMs(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(err instanceof Error ? err : new Error(String(err)), '[vault] observer notification failed')\n : undefined,\n );\n\n // Per-table live-count cache. Populated lazily on first count() call.\n // Invalidated on every mutation via notifyMutation.\n const countCache = new Map<string, number>();\n\n const getCachedCount = async (table: keyof S & string): Promise<number> => {\n if (countCache.has(table)) return countCache.get(table)!;\n\n const n = await core.count(table);\n\n countCache.set(table, n);\n\n return n;\n };\n\n const notifyMutation = (table: keyof S & string): void => {\n countCache.delete(table); // invalidate on any mutation\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 need to invalidate the count cache and notify observers.\n const notifyExternal = (table: keyof S & string): void => {\n countCache.delete(table);\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 countCache.set(table, 0); // we know count is 0 after clear\n },\n\n async count(table) {\n checkDisposed();\n\n return timed(table, 'count', () => getCachedCount(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 // Warm the count cache so subsequent adapter.count() calls don't re-hit the backend.\n countCache.set(name, live);\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 getCachedCount(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(tables?: readonly (keyof S & string)[]) {\n checkDisposed();\n\n const tableNames = tables ?? (Object.keys(schema) as Array<keyof S & string>);\n\n if (!tables && pruneAll) {\n const result = await pruneAll();\n\n for (const [name, count] of Object.entries(result)) {\n if (count > 0) countCache.delete(name);\n }\n\n return result as { [K in keyof S & string]: number };\n }\n\n const pairs = await Promise.all(\n tableNames.map(async (name) => {\n const pruned = await core.pruneExpiredInTable(name);\n\n if (pruned > 0) countCache.delete(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,8 +1,11 @@
1
- import type { Adapter, AnySchema, BaseAdapterOptions, RecordOf, TransactionContext } from './types';
1
+ import type { AnySchema, BaseAdapterOptions, IndexedDbVaultStore, RecordOf, TransactionContext, VaultStore } from './types';
2
2
  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>;
3
+ /** Builds the portable surface first; IndexedDB opts into transactions at its factory boundary. */
3
4
  export declare function buildAdapterOps<S extends AnySchema>(schema: S, core: StorageBackend<S>, options?: BaseAdapterOptions<S> & {
4
- buildBatch?: (deps: BatchDeps<S>) => BatchImpl<S>;
5
5
  onCrossTabMessage?: (notify: (table: keyof S & string) => void) => (() => void) | undefined;
6
6
  onMutation?: (table: keyof S & string) => void;
7
- }): Adapter<S>;
7
+ onTransactions?: (deps: BatchDeps<S>) => void;
8
+ }): VaultStore<S>;
9
+ /** IndexedDB is the only backend that can bind this callback to one native transaction. */
10
+ export declare function withIndexedDbTransactions<S extends AnySchema>(store: VaultStore<S>, batch: BatchImpl<S>): IndexedDbVaultStore<S>;
8
11
  //# sourceMappingURL=adapter-core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-core.d.ts","sourceRoot":"","sources":["../src/adapter-core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,kBAAkB,EAIlB,QAAQ,EACR,kBAAkB,EAEnB,MAAM,SAAS,CAAC;AAkJjB,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,CAkK1B;AAID,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,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC;IAClD,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;CAChD,GACA,OAAO,CAAC,CAAC,CAAC,CA8UZ"}
1
+ {"version":3,"file":"adapter-core.d.ts","sourceRoot":"","sources":["../src/adapter-core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAElB,mBAAmB,EAGnB,QAAQ,EACR,kBAAkB,EAElB,UAAU,EACX,MAAM,SAAS,CAAC;AAiJjB,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,CAkRf;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"}