@vielzeug/vault 1.0.4 → 2.0.1

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 -5
  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/dist/index.js CHANGED
@@ -1,11 +1,9 @@
1
1
  import { VaultDisposedError as e, VaultError as t, VaultMigrationError as n, VaultQuotaError as r, VaultScopeError as i } from "./errors.js";
2
- import { defaultCodec as a, isExpired as o, ttl as s } from "./ttl.js";
3
- import { createIndexedDB as c } from "./adapters/indexeddb.js";
4
- import { createMemory as l } from "./adapters/memory.js";
5
- import { createLocalStorage as u, createSessionStorage as d } from "./adapters/webstorage.js";
6
- import { defineMigration as f } from "./migration.js";
7
- import { scheduleExpiredPrune as p } from "./prune.js";
8
- import { toReadableStream as m } from "./streaming.js";
9
- import { createVersionedCodec as h } from "./versioned-codec.js";
10
- import { table as g } from "./types.js";
11
- export { e as VaultDisposedError, t as VaultError, n as VaultMigrationError, r as VaultQuotaError, i as VaultScopeError, c as createIndexedDB, u as createLocalStorage, l as createMemory, d as createSessionStorage, h as createVersionedCodec, a as defaultCodec, f as defineMigration, o as isExpired, p as scheduleExpiredPrune, g as table, m as toReadableStream, s as ttl };
2
+ import { isExpired as a, ttl as o } from "./ttl.js";
3
+ import { createIndexedDB as s } from "./adapters/indexeddb.js";
4
+ import { createMemory as c } from "./adapters/memory.js";
5
+ import { createLocalStorage as l, createSessionStorage as u } from "./adapters/webstorage.js";
6
+ import { defineMigration as d } from "./migration.js";
7
+ import { scheduleExpiredPrune as f } from "./prune.js";
8
+ import { table as p } from "./types.js";
9
+ export { e as VaultDisposedError, t as VaultError, n as VaultMigrationError, r as VaultQuotaError, i as VaultScopeError, s as createIndexedDB, l as createLocalStorage, c as createMemory, u as createSessionStorage, d as defineMigration, a as isExpired, f as scheduleExpiredPrune, p as table, o as ttl };
package/dist/internal.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_dev.cjs"),t=require("./errors.cjs");var n=`\0`;function r(e){return`${encodeURIComponent(e)}${n}`}function i(e,t,r){return`${encodeURIComponent(e)}${n}${encodeURIComponent(t)}${n}${encodeURIComponent(r)}`}function a(e,t){return`${encodeURIComponent(e)}${n}${encodeURIComponent(t)}${n}`}function o(e,t){if(!t)return;let i=r(e);if(!t.startsWith(i))return;let a=t.slice(i.length),o=a.indexOf(n);if(o!==-1)try{return decodeURIComponent(a.slice(0,o))}catch{return}}function s(n,r){let i=new Map,a=!1,o=t=>{r?r(t):e.error(`observer notification failed`,t)},s=e=>{if(a)return;let t=i.get(e);!t||t.size===0||n(e).then(t=>{if(a)return;let n=i.get(e);if(!(!n||n.size===0))for(let e of n)try{e(t)}catch(e){o(e)}}).catch(e=>o(e))};return{dispose:()=>{a=!0,i.clear()},notify:s,observe:(e,n,{immediate:r=!0,signal:o}={})=>{if(a)throw new t.VaultDisposedError(`observer hub is disposed`);if(o?.aborted)return()=>{};let c=e,l=n,u=i.get(c);u||(u=new Set,i.set(c,u)),u.add(l),r&&s(e);let d=()=>{let e=i.get(c);e&&(e.delete(l),e.size===0&&i.delete(c))};return o?.addEventListener(`abort`,d,{once:!0}),d}}}function c(e,t=`latest`,n){return{[Symbol.asyncIterator](){let r=[],i=null,a=n?.aborted??!1,o=null,s=()=>{a||(a=!0,o?.(),o=null,i&&=(i({done:!0,value:void 0}),null))};return n?.addEventListener(`abort`,s,{once:!0}),a||(o=e(e=>{if(!a)if(i){let t=i;i=null,t({done:!1,value:e})}else t===`all`?r.push(e):r=[e]})),{async next(){return a?{done:!0,value:void 0}:r.length>0?{done:!1,value:r.shift()}:new Promise(e=>{i=e})},async return(){return s(),{done:!0,value:void 0}},async throw(e){return s(),Promise.reject(e)}}}}}function l(n){return function(r,i,{eager:a=!1,signal:o}={}){let s=[...new Set(r)];if(s.length===0)throw new t.VaultScopeError(`observeMany requires at least one table`);if(s.length<r.length&&e.warn(`observeMany: duplicate table names were ignored`),o?.aborted)return()=>{};let c=new Map,l=!1,u=!1,d=()=>Object.fromEntries(r.map(e=>[e,c.get(e)??[]])),f=()=>{l||!a&&c.size<s.length||(l=!0,queueMicrotask(()=>{l=!1,u||i(d())}))},p=s.map(e=>n.observe(e,t=>{c.set(e,t),f()})),m=()=>{u=!0;for(let e of p)e()};return o?.addEventListener(`abort`,m,{once:!0}),m}}function u(e,n,r){let i=String(e[n].key),a=r[i];if(a==null)throw new t.VaultError(`key field "${i}" in table "${String(n)}" must be a non-null value, got ${String(a)}`);return a}exports.createObserveMany=l,exports.createObserverHub=s,exports.createWatchIterable=c,exports.decodeStorageTableFromKey=o,exports.encodeDbPrefix=r,exports.encodeStorageKey=i,exports.encodeStorageTablePrefix=a,exports.getRecordKey=u;
1
+ const e=require("./errors.cjs");require("./_dev.cjs");var t=`\0`;function n(t){if(typeof t==`number`){if(!Number.isFinite(t))throw new e.VaultError(`primary key must be a finite number, received ${String(t)}`);return`n:${String(t)}`}return`s:${encodeURIComponent(t)}`}function r(e){return`${encodeURIComponent(e)}${t}`}function i(e,r,i){return`${encodeURIComponent(e)}${t}${encodeURIComponent(r)}${t}${n(i)}`}function a(e,n){return`${encodeURIComponent(e)}${t}${encodeURIComponent(n)}${t}`}function o(e,n){if(!n)return;let i=r(e);if(!n.startsWith(i))return;let a=n.slice(i.length),o=a.indexOf(t);if(o!==-1)try{return decodeURIComponent(a.slice(0,o))}catch{return}}function s(t,n){let r=new Map,i=!1,a=e=>{n&&n(e)},o=e=>{if(i)return;let n=r.get(e);!n||n.size===0||t(e).then(t=>{if(i)return;let n=r.get(e);if(!(!n||n.size===0))for(let e of n)try{e(t)}catch(e){a(e)}}).catch(a)};return{dispose:()=>{i=!0,r.clear()},notify:o,observe:(t,n,{immediate:a=!0,signal:s}={})=>{if(i)throw new e.VaultDisposedError(`observer hub is disposed`);if(s?.aborted)return()=>{};let c=n,l=r.get(t)??new Set;r.set(t,l),l.add(c),a&&o(t);let u=()=>{let e=r.get(t);e&&(e.delete(c),e.size===0&&r.delete(t))};return s?.addEventListener(`abort`,u,{once:!0}),u}}}function c(t,n,r){let i=String(t[n].key),a=r[i];if(typeof a!=`number`&&typeof a!=`string`)throw new e.VaultError(`record in table "${String(n)}" must have a string or finite-number key at "${i}"`);if(typeof a==`number`&&!Number.isFinite(a))throw new e.VaultError(`record in table "${String(n)}" must have a finite-number key at "${i}"`);return a}exports.createObserverHub=s,exports.decodeStorageTableFromKey=o,exports.encodeDbPrefix=r,exports.encodeStorageKey=i,exports.encodeStorageTablePrefix=a,exports.encodeVaultKey=n,exports.getRecordKey=c;
2
2
  //# sourceMappingURL=internal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.cjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import type { AnySchema, KeyOf, RecordOf } from './types';\n\nimport { error as logError, warn } from './_dev';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\n/**\n * Separator used between encoded key segments. The null byte (\\x00) is safe\n * because `encodeURIComponent` always percent-encodes it to `%00` (a 3-character\n * string), so the raw `\\x00` byte can never appear inside an encoded segment —\n * eliminating key-collision risk that a URL-safe character like `~` would carry.\n */\nconst SEP = '\\x00';\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeURIComponent(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) {\n onError(error);\n } else {\n logError('observer notification failed', error);\n }\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch((error) => reportObserverError(error));\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) {\n throw new VaultDisposedError('observer hub is disposed');\n }\n\n if (signal?.aborted) return () => {};\n\n const key = table;\n const wrapped = listener as ObserverListener<unknown>;\n let listeners = observers.get(key);\n\n if (!listeners) {\n listeners = new Set();\n observers.set(key, listeners);\n }\n\n listeners.add(wrapped);\n\n // Fire an initial snapshot unless the caller opted out.\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(key);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(key);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n const dispose = (): void => {\n disposed = true;\n observers.clear();\n };\n\n return { dispose, notify, observe };\n}\n\n/**\n * Creates an AsyncIterable that yields a snapshot on every mutation.\n * The first snapshot is emitted immediately (on the first `next()` call).\n *\n * `mode` controls back-pressure:\n * - `'latest'` (default): if the consumer lags, only the most recent snapshot is retained.\n * Intermediate states are discarded. Best for rendering/display use-cases.\n * - `'all'`: every snapshot is queued and delivered in order. Use when every intermediate\n * state matters (audit trails, animation).\n *\n * Pass an `AbortSignal` to cancel the iteration externally:\n * ```ts\n * const controller = new AbortController();\n * for await (const users of db.watch('users', { signal: controller.signal })) { ... }\n * controller.abort(); // stops the loop\n * ```\n *\n * **Resource note:** The observer subscription is created eagerly on `[Symbol.asyncIterator]()` to\n * avoid missing mutations before the first `next()` call. Always consume the iterator to completion,\n * `break`/`return` out of `for await`, or pass an `AbortSignal` — otherwise the subscription\n * will remain active until the adapter is disposed.\n */\nexport function createWatchIterable<T>(\n subscribe: (listener: (snapshot: T[]) => void, signal?: AbortSignal) => () => void,\n mode: 'all' | 'latest' = 'latest',\n signal?: AbortSignal,\n): AsyncIterable<T[]> {\n return {\n [Symbol.asyncIterator]() {\n let pending: T[][] = [];\n let waiting: ((value: IteratorResult<T[]>) => void) | null = null;\n let done = signal?.aborted ?? false;\n let unsubscribe: (() => void) | null = null;\n\n const finish = (): void => {\n if (done) return;\n\n done = true;\n unsubscribe?.();\n unsubscribe = null;\n\n if (waiting) {\n waiting({ done: true, value: undefined });\n waiting = null;\n }\n };\n\n signal?.addEventListener('abort', finish, { once: true });\n\n // Eager registration: subscribe immediately so mutations between iterator\n // creation and first next() are not silently lost.\n if (!done) {\n unsubscribe = subscribe((snapshot) => {\n if (done) return;\n\n if (waiting) {\n const resolve = waiting;\n\n waiting = null;\n resolve({ done: false, value: snapshot });\n } else if (mode === 'all') {\n pending.push(snapshot);\n } else {\n pending = [snapshot];\n }\n });\n }\n\n return {\n async next(): Promise<IteratorResult<T[]>> {\n if (done) return { done: true, value: undefined };\n\n if (pending.length > 0) {\n const value = pending.shift()!;\n\n return { done: false, value };\n }\n\n return new Promise<IteratorResult<T[]>>((resolve) => {\n waiting = resolve;\n });\n },\n\n async return(): Promise<IteratorResult<T[]>> {\n finish();\n\n return { done: true, value: undefined };\n },\n\n async throw(err?: unknown): Promise<IteratorResult<T[]>> {\n finish();\n\n return Promise.reject(err);\n },\n };\n },\n };\n}\n\n/**\n * Observes multiple tables simultaneously. The listener receives a combined snapshot\n * `{ [tableName]: RecordOf<S, T>[] }` and fires once after all tables have delivered\n * their first snapshot. Subsequent firings are coalesced per microtask.\n *\n * Since `observe()` always fires immediately, each per-table observer populates the\n * snapshotMap on registration. No separate prefetch step is needed.\n */\nexport function createObserveMany<S extends AnySchema>(hub: ReturnType<typeof createObserverHub<S>>) {\n return function observeMany<K extends keyof S & string>(\n tables: readonly K[],\n listener: (snapshots: { [T in K]: RecordOf<S, T>[] }) => void,\n { eager = false, signal }: { eager?: boolean; signal?: AbortSignal } = {},\n ): () => void {\n const distinctTables = [...new Set(tables)] as K[];\n\n if (distinctTables.length === 0) throw new VaultScopeError('observeMany requires at least one table');\n\n if (distinctTables.length < tables.length) {\n warn('observeMany: duplicate table names were ignored');\n }\n\n if (signal?.aborted) return () => {};\n\n const snapshotMap = new Map<string, RecordOf<S, K>[]>();\n let microtaskQueued = false;\n let stopped = false;\n\n const buildCombined = (): { [T in K]: RecordOf<S, T>[] } =>\n Object.fromEntries(tables.map((t) => [t, snapshotMap.get(t) ?? []])) as { [T in K]: RecordOf<S, T>[] };\n\n const scheduleFlush = (): void => {\n // In eager mode fire as soon as any table has delivered; in default mode wait for all.\n if (microtaskQueued || (!eager && snapshotMap.size < distinctTables.length)) return;\n\n microtaskQueued = true;\n queueMicrotask(() => {\n microtaskQueued = false;\n\n if (!stopped) listener(buildCombined());\n });\n };\n\n // Register observers — each fires an immediate snapshot that populates snapshotMap.\n // Once all tables have delivered, scheduleFlush emits the first combined snapshot.\n // Signal is NOT passed to inner hub.observe() calls — the outer stop() is the single\n // cleanup path, avoiding O(tables) redundant abort listeners per signal.\n const stopFns = distinctTables.map((t) =>\n hub.observe(t, (records) => {\n snapshotMap.set(t, records as RecordOf<S, K>[]);\n scheduleFlush();\n }),\n );\n\n const stop = (): void => {\n stopped = true;\n\n for (const s of stopFns) s();\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (keyValue === undefined || keyValue === null) {\n throw new VaultError(\n `key field \"${keyField}\" in table \"${String(table)}\" must be a non-null value, got ${String(keyValue)}`,\n );\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"internal.cjs","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';\n\nimport { error as logError } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\nconst SEP = '\\x00';\n\n/**\n * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while\n * keeping the same portable primary-key contract as IndexedDB.\n */\nexport function encodeVaultKey(key: VaultKey): string {\n if (typeof key === 'number') {\n if (!Number.isFinite(key)) throw new VaultError(`primary key must be a finite number, received ${String(key)}`);\n\n return `n:${String(key)}`;\n }\n\n return `s:${encodeURIComponent(key)}`;\n}\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: VaultKey): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeVaultKey(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\n/** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) onError(error);\n else logError('observer notification failed', error);\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch(reportObserverError);\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) throw new VaultDisposedError('observer hub is disposed');\n\n if (signal?.aborted) return () => {};\n\n const wrapped = listener as ObserverListener<unknown>;\n const listeners = observers.get(table) ?? new Set<ObserverListener<unknown>>();\n\n observers.set(table, listeners);\n listeners.add(wrapped);\n\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(table);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(table);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n return {\n dispose: () => {\n disposed = true;\n observers.clear();\n },\n notify,\n observe,\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (typeof keyValue !== 'number' && typeof keyValue !== 'string') {\n throw new VaultError(`record in table \"${String(table)}\" must have a string or finite-number key at \"${keyField}\"`);\n }\n\n if (typeof keyValue === 'number' && !Number.isFinite(keyValue)) {\n throw new VaultError(`record in table \"${String(table)}\" must have a finite-number key at \"${keyField}\"`);\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":"sDAOA,IAAM,EAAM,KAMZ,SAAgB,EAAe,EAAuB,CACpD,GAAI,OAAO,GAAQ,SAAU,CAC3B,GAAI,CAAC,OAAO,SAAS,CAAG,EAAG,MAAM,IAAI,EAAA,WAAW,iDAAiD,OAAO,CAAG,GAAG,EAE9G,MAAO,KAAK,OAAO,CAAG,GACxB,CAEA,MAAO,KAAK,mBAAmB,CAAG,GACpC,CAEA,SAAgB,EAAe,EAAwB,CACrD,MAAO,GAAG,mBAAmB,CAAM,IAAI,GACzC,CAEA,SAAgB,EAAiB,EAAgB,EAAe,EAAuB,CACrF,MAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,IAAM,EAAe,CAAG,GACnG,CAEA,SAAgB,EAAyB,EAAgB,EAAuB,CAC9E,MAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,GAC3E,CAEA,SAAgB,EAA0B,EAAgB,EAA+C,CACvG,GAAI,CAAC,EAAY,OAEjB,IAAM,EAAS,EAAe,CAAM,EAEpC,GAAI,CAAC,EAAW,WAAW,CAAM,EAAG,OAEpC,IAAM,EAAO,EAAW,MAAM,EAAO,MAAM,EACrC,EAAM,EAAK,QAAQ,CAAG,EAExB,OAAQ,GAEZ,GAAI,CACF,OAAO,mBAAmB,EAAK,MAAM,EAAG,CAAG,CAAC,CAC9C,MAAQ,CACN,MACF,CACF,CAGA,SAAgB,EACd,EACA,EACA,CACA,IAAM,EAAY,IAAI,IAClB,EAAW,GAET,EAAuB,GAAyB,CAChD,GAAS,EAAQ,CAAK,CAE5B,EAEM,EAAsC,GAAmB,CAC7D,GAAI,EAAU,OAEd,IAAM,EAAY,EAAU,IAAI,CAAK,EAEjC,CAAC,GAAa,EAAU,OAAS,GAErC,EAAY,CAAK,CAAC,CACf,KAAM,GAAY,CACjB,GAAI,EAAU,OAEd,IAAM,EAAU,EAAU,IAAI,CAAK,EAE/B,MAAC,GAAW,EAAQ,OAAS,GAEjC,IAAK,IAAM,KAAY,EACrB,GAAI,CACF,EAAS,CAAoB,CAC/B,OAAS,EAAO,CACd,EAAoB,CAAK,CAC3B,CAEJ,CAAC,CAAC,CACD,MAAM,CAAmB,CAC9B,EAkCA,MAAO,CACL,YAAe,CACb,EAAW,GACX,EAAU,MAAM,CAClB,EACA,SACA,SArCA,EACA,EACA,CAAE,YAAY,GAAM,UAA0D,CAAC,IAC9D,CACjB,GAAI,EAAU,MAAM,IAAI,EAAA,mBAAmB,0BAA0B,EAErE,GAAI,GAAQ,QAAS,UAAa,CAAC,EAEnC,IAAM,EAAU,EACV,EAAY,EAAU,IAAI,CAAK,GAAK,IAAI,IAE9C,EAAU,IAAI,EAAO,CAAS,EAC9B,EAAU,IAAI,CAAO,EAEjB,GAAW,EAAO,CAAK,EAE3B,IAAM,MAAmB,CACvB,IAAM,EAAU,EAAU,IAAI,CAAK,EAE9B,IAEL,EAAQ,OAAO,CAAO,EAElB,EAAQ,OAAS,GAAG,EAAU,OAAO,CAAK,EAChD,EAIA,OAFA,GAAQ,iBAAiB,QAAS,EAAM,CAAE,KAAM,EAAK,CAAC,EAE/C,CACT,CASA,CACF,CAEA,SAAgB,EACd,EACA,EACA,EACa,CACb,IAAM,EAAW,OAAO,EAAO,EAAM,CAAC,GAAG,EACnC,EAAY,EAAkC,GAEpD,GAAI,OAAO,GAAa,UAAY,OAAO,GAAa,SACtD,MAAM,IAAI,EAAA,WAAW,oBAAoB,OAAO,CAAK,EAAE,gDAAgD,EAAS,EAAE,EAGpH,GAAI,OAAO,GAAa,UAAY,CAAC,OAAO,SAAS,CAAQ,EAC3D,MAAM,IAAI,EAAA,WAAW,oBAAoB,OAAO,CAAK,EAAE,sCAAsC,EAAS,EAAE,EAG1G,OAAO,CACT"}
@@ -1,8 +1,14 @@
1
- import type { AnySchema, KeyOf, RecordOf } from './types';
1
+ import type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';
2
+ /**
3
+ * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while
4
+ * keeping the same portable primary-key contract as IndexedDB.
5
+ */
6
+ export declare function encodeVaultKey(key: VaultKey): string;
2
7
  export declare function encodeDbPrefix(dbName: string): string;
3
- export declare function encodeStorageKey(dbName: string, table: string, key: string): string;
8
+ export declare function encodeStorageKey(dbName: string, table: string, key: VaultKey): string;
4
9
  export declare function encodeStorageTablePrefix(dbName: string, table: string): string;
5
10
  export declare function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined;
11
+ /** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */
6
12
  export declare function createObserverHub<S extends AnySchema>(getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>, onError?: (error: unknown) => void): {
7
13
  dispose: () => void;
8
14
  notify: <K extends keyof S & string>(table: K) => void;
@@ -11,40 +17,5 @@ export declare function createObserverHub<S extends AnySchema>(getAll: <K extend
11
17
  signal?: AbortSignal;
12
18
  }) => (() => void);
13
19
  };
14
- /**
15
- * Creates an AsyncIterable that yields a snapshot on every mutation.
16
- * The first snapshot is emitted immediately (on the first `next()` call).
17
- *
18
- * `mode` controls back-pressure:
19
- * - `'latest'` (default): if the consumer lags, only the most recent snapshot is retained.
20
- * Intermediate states are discarded. Best for rendering/display use-cases.
21
- * - `'all'`: every snapshot is queued and delivered in order. Use when every intermediate
22
- * state matters (audit trails, animation).
23
- *
24
- * Pass an `AbortSignal` to cancel the iteration externally:
25
- * ```ts
26
- * const controller = new AbortController();
27
- * for await (const users of db.watch('users', { signal: controller.signal })) { ... }
28
- * controller.abort(); // stops the loop
29
- * ```
30
- *
31
- * **Resource note:** The observer subscription is created eagerly on `[Symbol.asyncIterator]()` to
32
- * avoid missing mutations before the first `next()` call. Always consume the iterator to completion,
33
- * `break`/`return` out of `for await`, or pass an `AbortSignal` — otherwise the subscription
34
- * will remain active until the adapter is disposed.
35
- */
36
- export declare function createWatchIterable<T>(subscribe: (listener: (snapshot: T[]) => void, signal?: AbortSignal) => () => void, mode?: 'all' | 'latest', signal?: AbortSignal): AsyncIterable<T[]>;
37
- /**
38
- * Observes multiple tables simultaneously. The listener receives a combined snapshot
39
- * `{ [tableName]: RecordOf<S, T>[] }` and fires once after all tables have delivered
40
- * their first snapshot. Subsequent firings are coalesced per microtask.
41
- *
42
- * Since `observe()` always fires immediately, each per-table observer populates the
43
- * snapshotMap on registration. No separate prefetch step is needed.
44
- */
45
- export declare function createObserveMany<S extends AnySchema>(hub: ReturnType<typeof createObserverHub<S>>): <K extends keyof S & string>(tables: readonly K[], listener: (snapshots: { [T in K]: RecordOf<S, T>[]; }) => void, { eager, signal }?: {
46
- eager?: boolean;
47
- signal?: AbortSignal;
48
- }) => () => void;
49
20
  export declare function getRecordKey<S extends AnySchema, K extends keyof S>(schema: S, table: K, value: RecordOf<S, K>): KeyOf<S, K>;
50
21
  //# sourceMappingURL=internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAe1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAiBvG;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,SAAS,EACnD,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;mBA+Ed,IAAI;aAlER,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,KAAG,IAAI;cA0B1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAClC,CAAC,YACE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,0BACf;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAC1E,CAAC,MAAM,IAAI,CAAC;EA0ChB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,MAAM,IAAI,EAClF,IAAI,GAAE,KAAK,GAAG,QAAmB,EACjC,MAAM,CAAC,EAAE,WAAW,GACnB,aAAa,CAAC,CAAC,EAAE,CAAC,CAuEpB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,IACrE,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EACpD,QAAQ,SAAS,CAAC,EAAE,EACpB,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAE,KAAK,IAAI,EAC7D,oBAA2B;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,KACxE,MAAM,IAAI,CAmDd;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,MAAM,CAAC,EACjE,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GACpB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAWb"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AASpE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAQpD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM,CAErF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAiBvG;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,SAAS,EACnD,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;;aAUlB,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,KAAG,IAAI;cA0B1C,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAClC,CAAC,YACE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,IAAI,0BACf;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAC1E,CAAC,MAAM,IAAI,CAAC;EAoChB;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,MAAM,CAAC,EACjE,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GACpB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAab"}
package/dist/internal.js CHANGED
@@ -1,125 +1,75 @@
1
- import { error as e, warn as t } from "./_dev.js";
2
- import { VaultDisposedError as n, VaultError as r, VaultScopeError as i } from "./errors.js";
1
+ import { VaultDisposedError as e, VaultError as t } from "./errors.js";
2
+ import "./_dev.js";
3
3
  //#region src/internal.ts
4
- var a = "\0";
5
- function o(e) {
6
- return `${encodeURIComponent(e)}${a}`;
4
+ var n = "\0";
5
+ function r(e) {
6
+ if (typeof e == "number") {
7
+ if (!Number.isFinite(e)) throw new t(`primary key must be a finite number, received ${String(e)}`);
8
+ return `n:${String(e)}`;
9
+ }
10
+ return `s:${encodeURIComponent(e)}`;
11
+ }
12
+ function i(e) {
13
+ return `${encodeURIComponent(e)}${n}`;
7
14
  }
8
- function s(e, t, n) {
9
- return `${encodeURIComponent(e)}${a}${encodeURIComponent(t)}${a}${encodeURIComponent(n)}`;
15
+ function a(e, t, i) {
16
+ return `${encodeURIComponent(e)}${n}${encodeURIComponent(t)}${n}${r(i)}`;
10
17
  }
11
- function c(e, t) {
12
- return `${encodeURIComponent(e)}${a}${encodeURIComponent(t)}${a}`;
18
+ function o(e, t) {
19
+ return `${encodeURIComponent(e)}${n}${encodeURIComponent(t)}${n}`;
13
20
  }
14
- function l(e, t) {
21
+ function s(e, t) {
15
22
  if (!t) return;
16
- let n = o(e);
17
- if (!t.startsWith(n)) return;
18
- let r = t.slice(n.length), i = r.indexOf(a);
19
- if (i !== -1) try {
20
- return decodeURIComponent(r.slice(0, i));
23
+ let r = i(e);
24
+ if (!t.startsWith(r)) return;
25
+ let a = t.slice(r.length), o = a.indexOf(n);
26
+ if (o !== -1) try {
27
+ return decodeURIComponent(a.slice(0, o));
21
28
  } catch {
22
29
  return;
23
30
  }
24
31
  }
25
- function u(t, r) {
26
- let i = /* @__PURE__ */ new Map(), a = !1, o = (t) => {
27
- r ? r(t) : e("observer notification failed", t);
28
- }, s = (e) => {
29
- if (a) return;
30
- let n = i.get(e);
32
+ function c(t, n) {
33
+ let r = /* @__PURE__ */ new Map(), i = !1, a = (e) => {
34
+ n && n(e);
35
+ }, o = (e) => {
36
+ if (i) return;
37
+ let n = r.get(e);
31
38
  !n || n.size === 0 || t(e).then((t) => {
32
- if (a) return;
33
- let n = i.get(e);
39
+ if (i) return;
40
+ let n = r.get(e);
34
41
  if (!(!n || n.size === 0)) for (let e of n) try {
35
42
  e(t);
36
43
  } catch (e) {
37
- o(e);
44
+ a(e);
38
45
  }
39
- }).catch((e) => o(e));
46
+ }).catch(a);
40
47
  };
41
48
  return {
42
49
  dispose: () => {
43
- a = !0, i.clear();
50
+ i = !0, r.clear();
44
51
  },
45
- notify: s,
46
- observe: (e, t, { immediate: r = !0, signal: o } = {}) => {
47
- if (a) throw new n("observer hub is disposed");
48
- if (o?.aborted) return () => {};
49
- let c = e, l = t, u = i.get(c);
50
- u || (u = /* @__PURE__ */ new Set(), i.set(c, u)), u.add(l), r && s(e);
51
- let d = () => {
52
- let e = i.get(c);
53
- e && (e.delete(l), e.size === 0 && i.delete(c));
52
+ notify: o,
53
+ observe: (t, n, { immediate: a = !0, signal: s } = {}) => {
54
+ if (i) throw new e("observer hub is disposed");
55
+ if (s?.aborted) return () => {};
56
+ let c = n, l = r.get(t) ?? /* @__PURE__ */ new Set();
57
+ r.set(t, l), l.add(c), a && o(t);
58
+ let u = () => {
59
+ let e = r.get(t);
60
+ e && (e.delete(c), e.size === 0 && r.delete(t));
54
61
  };
55
- return o?.addEventListener("abort", d, { once: !0 }), d;
62
+ return s?.addEventListener("abort", u, { once: !0 }), u;
56
63
  }
57
64
  };
58
65
  }
59
- function d(e, t = "latest", n) {
60
- return { [Symbol.asyncIterator]() {
61
- let r = [], i = null, a = n?.aborted ?? !1, o = null, s = () => {
62
- a || (a = !0, o?.(), o = null, i &&= (i({
63
- done: !0,
64
- value: void 0
65
- }), null));
66
- };
67
- return n?.addEventListener("abort", s, { once: !0 }), a || (o = e((e) => {
68
- if (!a) if (i) {
69
- let t = i;
70
- i = null, t({
71
- done: !1,
72
- value: e
73
- });
74
- } else t === "all" ? r.push(e) : r = [e];
75
- })), {
76
- async next() {
77
- return a ? {
78
- done: !0,
79
- value: void 0
80
- } : r.length > 0 ? {
81
- done: !1,
82
- value: r.shift()
83
- } : new Promise((e) => {
84
- i = e;
85
- });
86
- },
87
- async return() {
88
- return s(), {
89
- done: !0,
90
- value: void 0
91
- };
92
- },
93
- async throw(e) {
94
- return s(), Promise.reject(e);
95
- }
96
- };
97
- } };
98
- }
99
- function f(e) {
100
- return function(n, r, { eager: a = !1, signal: o } = {}) {
101
- let s = [...new Set(n)];
102
- if (s.length === 0) throw new i("observeMany requires at least one table");
103
- if (s.length < n.length && t("observeMany: duplicate table names were ignored"), o?.aborted) return () => {};
104
- let c = /* @__PURE__ */ new Map(), l = !1, u = !1, d = () => Object.fromEntries(n.map((e) => [e, c.get(e) ?? []])), f = () => {
105
- l || !a && c.size < s.length || (l = !0, queueMicrotask(() => {
106
- l = !1, u || r(d());
107
- }));
108
- }, p = s.map((t) => e.observe(t, (e) => {
109
- c.set(t, e), f();
110
- })), m = () => {
111
- u = !0;
112
- for (let e of p) e();
113
- };
114
- return o?.addEventListener("abort", m, { once: !0 }), m;
115
- };
116
- }
117
- function p(e, t, n) {
118
- let i = String(e[t].key), a = n[i];
119
- if (a == null) throw new r(`key field "${i}" in table "${String(t)}" must be a non-null value, got ${String(a)}`);
66
+ function l(e, n, r) {
67
+ let i = String(e[n].key), a = r[i];
68
+ if (typeof a != "number" && typeof a != "string") throw new t(`record in table "${String(n)}" must have a string or finite-number key at "${i}"`);
69
+ if (typeof a == "number" && !Number.isFinite(a)) throw new t(`record in table "${String(n)}" must have a finite-number key at "${i}"`);
120
70
  return a;
121
71
  }
122
72
  //#endregion
123
- export { f as createObserveMany, u as createObserverHub, d as createWatchIterable, l as decodeStorageTableFromKey, o as encodeDbPrefix, s as encodeStorageKey, c as encodeStorageTablePrefix, p as getRecordKey };
73
+ export { c as createObserverHub, s as decodeStorageTableFromKey, i as encodeDbPrefix, a as encodeStorageKey, o as encodeStorageTablePrefix, r as encodeVaultKey, l as getRecordKey };
124
74
 
125
75
  //# sourceMappingURL=internal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import type { AnySchema, KeyOf, RecordOf } from './types';\n\nimport { error as logError, warn } from './_dev';\nimport { VaultDisposedError, VaultError, VaultScopeError } from './errors';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\n/**\n * Separator used between encoded key segments. The null byte (\\x00) is safe\n * because `encodeURIComponent` always percent-encodes it to `%00` (a 3-character\n * string), so the raw `\\x00` byte can never appear inside an encoded segment —\n * eliminating key-collision risk that a URL-safe character like `~` would carry.\n */\nconst SEP = '\\x00';\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeURIComponent(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) {\n onError(error);\n } else {\n logError('observer notification failed', error);\n }\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch((error) => reportObserverError(error));\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) {\n throw new VaultDisposedError('observer hub is disposed');\n }\n\n if (signal?.aborted) return () => {};\n\n const key = table;\n const wrapped = listener as ObserverListener<unknown>;\n let listeners = observers.get(key);\n\n if (!listeners) {\n listeners = new Set();\n observers.set(key, listeners);\n }\n\n listeners.add(wrapped);\n\n // Fire an initial snapshot unless the caller opted out.\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(key);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(key);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n const dispose = (): void => {\n disposed = true;\n observers.clear();\n };\n\n return { dispose, notify, observe };\n}\n\n/**\n * Creates an AsyncIterable that yields a snapshot on every mutation.\n * The first snapshot is emitted immediately (on the first `next()` call).\n *\n * `mode` controls back-pressure:\n * - `'latest'` (default): if the consumer lags, only the most recent snapshot is retained.\n * Intermediate states are discarded. Best for rendering/display use-cases.\n * - `'all'`: every snapshot is queued and delivered in order. Use when every intermediate\n * state matters (audit trails, animation).\n *\n * Pass an `AbortSignal` to cancel the iteration externally:\n * ```ts\n * const controller = new AbortController();\n * for await (const users of db.watch('users', { signal: controller.signal })) { ... }\n * controller.abort(); // stops the loop\n * ```\n *\n * **Resource note:** The observer subscription is created eagerly on `[Symbol.asyncIterator]()` to\n * avoid missing mutations before the first `next()` call. Always consume the iterator to completion,\n * `break`/`return` out of `for await`, or pass an `AbortSignal` — otherwise the subscription\n * will remain active until the adapter is disposed.\n */\nexport function createWatchIterable<T>(\n subscribe: (listener: (snapshot: T[]) => void, signal?: AbortSignal) => () => void,\n mode: 'all' | 'latest' = 'latest',\n signal?: AbortSignal,\n): AsyncIterable<T[]> {\n return {\n [Symbol.asyncIterator]() {\n let pending: T[][] = [];\n let waiting: ((value: IteratorResult<T[]>) => void) | null = null;\n let done = signal?.aborted ?? false;\n let unsubscribe: (() => void) | null = null;\n\n const finish = (): void => {\n if (done) return;\n\n done = true;\n unsubscribe?.();\n unsubscribe = null;\n\n if (waiting) {\n waiting({ done: true, value: undefined });\n waiting = null;\n }\n };\n\n signal?.addEventListener('abort', finish, { once: true });\n\n // Eager registration: subscribe immediately so mutations between iterator\n // creation and first next() are not silently lost.\n if (!done) {\n unsubscribe = subscribe((snapshot) => {\n if (done) return;\n\n if (waiting) {\n const resolve = waiting;\n\n waiting = null;\n resolve({ done: false, value: snapshot });\n } else if (mode === 'all') {\n pending.push(snapshot);\n } else {\n pending = [snapshot];\n }\n });\n }\n\n return {\n async next(): Promise<IteratorResult<T[]>> {\n if (done) return { done: true, value: undefined };\n\n if (pending.length > 0) {\n const value = pending.shift()!;\n\n return { done: false, value };\n }\n\n return new Promise<IteratorResult<T[]>>((resolve) => {\n waiting = resolve;\n });\n },\n\n async return(): Promise<IteratorResult<T[]>> {\n finish();\n\n return { done: true, value: undefined };\n },\n\n async throw(err?: unknown): Promise<IteratorResult<T[]>> {\n finish();\n\n return Promise.reject(err);\n },\n };\n },\n };\n}\n\n/**\n * Observes multiple tables simultaneously. The listener receives a combined snapshot\n * `{ [tableName]: RecordOf<S, T>[] }` and fires once after all tables have delivered\n * their first snapshot. Subsequent firings are coalesced per microtask.\n *\n * Since `observe()` always fires immediately, each per-table observer populates the\n * snapshotMap on registration. No separate prefetch step is needed.\n */\nexport function createObserveMany<S extends AnySchema>(hub: ReturnType<typeof createObserverHub<S>>) {\n return function observeMany<K extends keyof S & string>(\n tables: readonly K[],\n listener: (snapshots: { [T in K]: RecordOf<S, T>[] }) => void,\n { eager = false, signal }: { eager?: boolean; signal?: AbortSignal } = {},\n ): () => void {\n const distinctTables = [...new Set(tables)] as K[];\n\n if (distinctTables.length === 0) throw new VaultScopeError('observeMany requires at least one table');\n\n if (distinctTables.length < tables.length) {\n warn('observeMany: duplicate table names were ignored');\n }\n\n if (signal?.aborted) return () => {};\n\n const snapshotMap = new Map<string, RecordOf<S, K>[]>();\n let microtaskQueued = false;\n let stopped = false;\n\n const buildCombined = (): { [T in K]: RecordOf<S, T>[] } =>\n Object.fromEntries(tables.map((t) => [t, snapshotMap.get(t) ?? []])) as { [T in K]: RecordOf<S, T>[] };\n\n const scheduleFlush = (): void => {\n // In eager mode fire as soon as any table has delivered; in default mode wait for all.\n if (microtaskQueued || (!eager && snapshotMap.size < distinctTables.length)) return;\n\n microtaskQueued = true;\n queueMicrotask(() => {\n microtaskQueued = false;\n\n if (!stopped) listener(buildCombined());\n });\n };\n\n // Register observers — each fires an immediate snapshot that populates snapshotMap.\n // Once all tables have delivered, scheduleFlush emits the first combined snapshot.\n // Signal is NOT passed to inner hub.observe() calls — the outer stop() is the single\n // cleanup path, avoiding O(tables) redundant abort listeners per signal.\n const stopFns = distinctTables.map((t) =>\n hub.observe(t, (records) => {\n snapshotMap.set(t, records as RecordOf<S, K>[]);\n scheduleFlush();\n }),\n );\n\n const stop = (): void => {\n stopped = true;\n\n for (const s of stopFns) s();\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (keyValue === undefined || keyValue === null) {\n throw new VaultError(\n `key field \"${keyField}\" in table \"${String(table)}\" must be a non-null value, got ${String(keyValue)}`,\n );\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"internal.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["import type { AnySchema, KeyOf, RecordOf, VaultKey } from './types';\n\nimport { error as logError } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\ntype ObserverListener<T> = (records: T[]) => void;\n\nconst SEP = '\\x00';\n\n/**\n * Type-tagged keys make `1` and `'1'` distinct in string-keyed adapters while\n * keeping the same portable primary-key contract as IndexedDB.\n */\nexport function encodeVaultKey(key: VaultKey): string {\n if (typeof key === 'number') {\n if (!Number.isFinite(key)) throw new VaultError(`primary key must be a finite number, received ${String(key)}`);\n\n return `n:${String(key)}`;\n }\n\n return `s:${encodeURIComponent(key)}`;\n}\n\nexport function encodeDbPrefix(dbName: string): string {\n return `${encodeURIComponent(dbName)}${SEP}`;\n}\n\nexport function encodeStorageKey(dbName: string, table: string, key: VaultKey): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}${encodeVaultKey(key)}`;\n}\n\nexport function encodeStorageTablePrefix(dbName: string, table: string): string {\n return `${encodeURIComponent(dbName)}${SEP}${encodeURIComponent(table)}${SEP}`;\n}\n\nexport function decodeStorageTableFromKey(dbName: string, storageKey: string | null): string | undefined {\n if (!storageKey) return undefined;\n\n const prefix = encodeDbPrefix(dbName);\n\n if (!storageKey.startsWith(prefix)) return undefined;\n\n const tail = storageKey.slice(prefix.length);\n const end = tail.indexOf(SEP);\n\n if (end === -1) return undefined;\n\n try {\n return decodeURIComponent(tail.slice(0, end));\n } catch {\n return undefined;\n }\n}\n\n/** `observe()` is Vault's single reactivity primitive: a snapshot now, then on mutations. */\nexport function createObserverHub<S extends AnySchema>(\n getAll: <K extends keyof S & string>(table: K) => Promise<RecordOf<S, K>[]>,\n onError?: (error: unknown) => void,\n) {\n const observers = new Map<string, Set<ObserverListener<unknown>>>();\n let disposed = false;\n\n const reportObserverError = (error: unknown): void => {\n if (onError) onError(error);\n else logError('observer notification failed', error);\n };\n\n const notify = <K extends keyof S & string>(table: K): void => {\n if (disposed) return;\n\n const listeners = observers.get(table);\n\n if (!listeners || listeners.size === 0) return;\n\n void getAll(table)\n .then((records) => {\n if (disposed) return;\n\n const current = observers.get(table);\n\n if (!current || current.size === 0) return;\n\n for (const listener of current) {\n try {\n listener(records as unknown[]);\n } catch (error) {\n reportObserverError(error);\n }\n }\n })\n .catch(reportObserverError);\n };\n\n const observe = <K extends keyof S & string>(\n table: K,\n listener: (records: RecordOf<S, K>[]) => void,\n { immediate = true, signal }: { immediate?: boolean; signal?: AbortSignal } = {},\n ): (() => void) => {\n if (disposed) throw new VaultDisposedError('observer hub is disposed');\n\n if (signal?.aborted) return () => {};\n\n const wrapped = listener as ObserverListener<unknown>;\n const listeners = observers.get(table) ?? new Set<ObserverListener<unknown>>();\n\n observers.set(table, listeners);\n listeners.add(wrapped);\n\n if (immediate) notify(table);\n\n const stop = (): void => {\n const current = observers.get(table);\n\n if (!current) return;\n\n current.delete(wrapped);\n\n if (current.size === 0) observers.delete(table);\n };\n\n signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n };\n\n return {\n dispose: () => {\n disposed = true;\n observers.clear();\n },\n notify,\n observe,\n };\n}\n\nexport function getRecordKey<S extends AnySchema, K extends keyof S>(\n schema: S,\n table: K,\n value: RecordOf<S, K>,\n): KeyOf<S, K> {\n const keyField = String(schema[table].key);\n const keyValue = (value as Record<string, unknown>)[keyField];\n\n if (typeof keyValue !== 'number' && typeof keyValue !== 'string') {\n throw new VaultError(`record in table \"${String(table)}\" must have a string or finite-number key at \"${keyField}\"`);\n }\n\n if (typeof keyValue === 'number' && !Number.isFinite(keyValue)) {\n throw new VaultError(`record in table \"${String(table)}\" must have a finite-number key at \"${keyField}\"`);\n }\n\n return keyValue as KeyOf<S, K>;\n}\n"],"mappings":";;;AAOA,IAAM,IAAM;AAMZ,SAAgB,EAAe,GAAuB;CACpD,IAAI,OAAO,KAAQ,UAAU;EAC3B,IAAI,CAAC,OAAO,SAAS,CAAG,GAAG,MAAM,IAAI,EAAW,iDAAiD,OAAO,CAAG,GAAG;EAE9G,OAAO,KAAK,OAAO,CAAG;CACxB;CAEA,OAAO,KAAK,mBAAmB,CAAG;AACpC;AAEA,SAAgB,EAAe,GAAwB;CACrD,OAAO,GAAG,mBAAmB,CAAM,IAAI;AACzC;AAEA,SAAgB,EAAiB,GAAgB,GAAe,GAAuB;CACrF,OAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI,IAAM,EAAe,CAAG;AACnG;AAEA,SAAgB,EAAyB,GAAgB,GAAuB;CAC9E,OAAO,GAAG,mBAAmB,CAAM,IAAI,IAAM,mBAAmB,CAAK,IAAI;AAC3E;AAEA,SAAgB,EAA0B,GAAgB,GAA+C;CACvG,IAAI,CAAC,GAAY;CAEjB,IAAM,IAAS,EAAe,CAAM;CAEpC,IAAI,CAAC,EAAW,WAAW,CAAM,GAAG;CAEpC,IAAM,IAAO,EAAW,MAAM,EAAO,MAAM,GACrC,IAAM,EAAK,QAAQ,CAAG;CAExB,UAAQ,IAEZ,IAAI;EACF,OAAO,mBAAmB,EAAK,MAAM,GAAG,CAAG,CAAC;CAC9C,QAAQ;EACN;CACF;AACF;AAGA,SAAgB,EACd,GACA,GACA;CACA,IAAM,oBAAY,IAAI,IAA4C,GAC9D,IAAW,IAET,KAAuB,MAAyB;EACpD,AAAI,KAAS,EAAQ,CAAK;CAE5B,GAEM,KAAsC,MAAmB;EAC7D,IAAI,GAAU;EAEd,IAAM,IAAY,EAAU,IAAI,CAAK;EAEjC,CAAC,KAAa,EAAU,SAAS,KAErC,EAAY,CAAK,CAAC,CACf,MAAM,MAAY;GACjB,IAAI,GAAU;GAEd,IAAM,IAAU,EAAU,IAAI,CAAK;GAE/B,OAAC,KAAW,EAAQ,SAAS,IAEjC,KAAK,IAAM,KAAY,GACrB,IAAI;IACF,EAAS,CAAoB;GAC/B,SAAS,GAAO;IACd,EAAoB,CAAK;GAC3B;EAEJ,CAAC,CAAC,CACD,MAAM,CAAmB;CAC9B;CAkCA,OAAO;EACL,eAAe;GAEb,AADA,IAAW,IACX,EAAU,MAAM;EAClB;EACA;EACA,UArCA,GACA,GACA,EAAE,eAAY,IAAM,cAA0D,CAAC,MAC9D;GACjB,IAAI,GAAU,MAAM,IAAI,EAAmB,0BAA0B;GAErE,IAAI,GAAQ,SAAS,aAAa,CAAC;GAEnC,IAAM,IAAU,GACV,IAAY,EAAU,IAAI,CAAK,qBAAK,IAAI,IAA+B;GAK7E,AAHA,EAAU,IAAI,GAAO,CAAS,GAC9B,EAAU,IAAI,CAAO,GAEjB,KAAW,EAAO,CAAK;GAE3B,IAAM,UAAmB;IACvB,IAAM,IAAU,EAAU,IAAI,CAAK;IAE9B,MAEL,EAAQ,OAAO,CAAO,GAElB,EAAQ,SAAS,KAAG,EAAU,OAAO,CAAK;GAChD;GAIA,OAFA,GAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC,GAE/C;EACT;CASA;AACF;AAEA,SAAgB,EACd,GACA,GACA,GACa;CACb,IAAM,IAAW,OAAO,EAAO,EAAM,CAAC,GAAG,GACnC,IAAY,EAAkC;CAEpD,IAAI,OAAO,KAAa,YAAY,OAAO,KAAa,UACtD,MAAM,IAAI,EAAW,oBAAoB,OAAO,CAAK,EAAE,gDAAgD,EAAS,EAAE;CAGpH,IAAI,OAAO,KAAa,YAAY,CAAC,OAAO,SAAS,CAAQ,GAC3D,MAAM,IAAI,EAAW,oBAAoB,OAAO,CAAK,EAAE,sCAAsC,EAAS,EAAE;CAG1G,OAAO;AACT"}
@@ -1,2 +1,2 @@
1
- function e(e){return({db:t,tx:n})=>{for(let r of e)switch(r.type){case`addIndex`:{let e=n.objectStore(r.table);e.indexNames.contains(r.field)||e.createIndex(r.field,`value.${r.field}`);break}case`addTable`:t.objectStoreNames.contains(r.name)||t.createObjectStore(r.name);break;case`removeIndex`:{let e=n.objectStore(r.table);e.indexNames.contains(r.field)&&e.deleteIndex(r.field);break}case`removeTable`:t.objectStoreNames.contains(r.name)&&t.deleteObjectStore(r.name);break}}}exports.defineMigration=e;
1
+ function e(e){return({db:t,tx:n})=>{for(let r of e)switch(r.type){case`addIndex`:{let e=n.objectStore(r.table);e.indexNames.contains(r.field)||e.createIndex(r.field,`value.${r.field}`);break}case`addTable`:t.objectStoreNames.contains(r.name)||t.createObjectStore(r.name);break;case`removeIndex`:{let e=n.objectStore(r.table);e.indexNames.contains(r.field)&&e.deleteIndex(r.field);break}case`removeTable`:t.objectStoreNames.contains(r.name)&&t.deleteObjectStore(r.name)}}}exports.defineMigration=e;
2
2
  //# sourceMappingURL=migration.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration.cjs","names":[],"sources":["../src/migration.ts"],"sourcesContent":["import type { MigrationFn } from './types';\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\n"],"mappings":"AA2BA,SAAgB,EAAgB,EAAqC,CACnE,OAAQ,CAAE,KAAI,QAAS,CACrB,IAAK,IAAM,KAAQ,EACjB,OAAQ,EAAK,KAAb,CACE,IAAK,WAAY,CACf,IAAM,EAAQ,EAAG,YAAY,EAAK,KAAK,EAGlC,EAAM,WAAW,SAAS,EAAK,KAAK,GACvC,EAAM,YAAY,EAAK,MAAO,SAAS,EAAK,OAAO,EAGrD,KACF,CACA,IAAK,WACE,EAAG,iBAAiB,SAAS,EAAK,IAAI,GACzC,EAAG,kBAAkB,EAAK,IAAI,EAGhC,MACF,IAAK,cAAe,CAClB,IAAM,EAAQ,EAAG,YAAY,EAAK,KAAK,EAEnC,EAAM,WAAW,SAAS,EAAK,KAAK,GACtC,EAAM,YAAY,EAAK,KAAK,EAG9B,KACF,CACA,IAAK,cACC,EAAG,iBAAiB,SAAS,EAAK,IAAI,GACxC,EAAG,kBAAkB,EAAK,IAAI,EAGhC,KACJ,CAEJ,CACF"}
1
+ {"version":3,"file":"migration.cjs","names":[],"sources":["../src/migration.ts"],"sourcesContent":["import type { MigrationFn } from './types';\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\n"],"mappings":"AA2BA,SAAgB,EAAgB,EAAqC,CACnE,OAAQ,CAAE,KAAI,QAAS,CACrB,IAAK,IAAM,KAAQ,EACjB,OAAQ,EAAK,KAAb,CACE,IAAK,WAAY,CACf,IAAM,EAAQ,EAAG,YAAY,EAAK,KAAK,EAGlC,EAAM,WAAW,SAAS,EAAK,KAAK,GACvC,EAAM,YAAY,EAAK,MAAO,SAAS,EAAK,OAAO,EAGrD,KACF,CACA,IAAK,WACE,EAAG,iBAAiB,SAAS,EAAK,IAAI,GACzC,EAAG,kBAAkB,EAAK,IAAI,EAGhC,MACF,IAAK,cAAe,CAClB,IAAM,EAAQ,EAAG,YAAY,EAAK,KAAK,EAEnC,EAAM,WAAW,SAAS,EAAK,KAAK,GACtC,EAAM,YAAY,EAAK,KAAK,EAG9B,KACF,CACA,IAAK,cACC,EAAG,iBAAiB,SAAS,EAAK,IAAI,GACxC,EAAG,kBAAkB,EAAK,IAAI,CAIpC,CAEJ,CACF"}
package/dist/migration.js CHANGED
@@ -15,9 +15,7 @@ function e(e) {
15
15
  e.indexNames.contains(r.field) && e.deleteIndex(r.field);
16
16
  break;
17
17
  }
18
- case "removeTable":
19
- t.objectStoreNames.contains(r.name) && t.deleteObjectStore(r.name);
20
- break;
18
+ case "removeTable": t.objectStoreNames.contains(r.name) && t.deleteObjectStore(r.name);
21
19
  }
22
20
  };
23
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"migration.js","names":[],"sources":["../src/migration.ts"],"sourcesContent":["import type { MigrationFn } from './types';\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\n"],"mappings":";AA2BA,SAAgB,EAAgB,GAAqC;CACnE,QAAQ,EAAE,OAAI,YAAS;EACrB,KAAK,IAAM,KAAQ,GACjB,QAAQ,EAAK,MAAb;GACE,KAAK,YAAY;IACf,IAAM,IAAQ,EAAG,YAAY,EAAK,KAAK;IAGvC,AAAK,EAAM,WAAW,SAAS,EAAK,KAAK,KACvC,EAAM,YAAY,EAAK,OAAO,SAAS,EAAK,OAAO;IAGrD;GACF;GACA,KAAK;IACH,AAAK,EAAG,iBAAiB,SAAS,EAAK,IAAI,KACzC,EAAG,kBAAkB,EAAK,IAAI;IAGhC;GACF,KAAK,eAAe;IAClB,IAAM,IAAQ,EAAG,YAAY,EAAK,KAAK;IAEvC,AAAI,EAAM,WAAW,SAAS,EAAK,KAAK,KACtC,EAAM,YAAY,EAAK,KAAK;IAG9B;GACF;GACA,KAAK;IACH,AAAI,EAAG,iBAAiB,SAAS,EAAK,IAAI,KACxC,EAAG,kBAAkB,EAAK,IAAI;IAGhC;EACJ;CAEJ;AACF"}
1
+ {"version":3,"file":"migration.js","names":[],"sources":["../src/migration.ts"],"sourcesContent":["import type { MigrationFn } from './types';\n\n/**\n * A single step in a typed migration definition.\n * Compose multiple steps to describe the full schema change between two versions.\n */\nexport type MigrationStep =\n | { field: string; table: string; type: 'addIndex' }\n | { field: string; table: string; type: 'removeIndex' }\n | { name: string; type: 'addTable' }\n | { name: string; type: 'removeTable' };\n\n/**\n * Builds a typed `MigrationFn` from a declarative list of schema change steps.\n * Each step is applied in order and is idempotent (safe to run when the target\n * already exists or has already been removed).\n *\n * ```ts\n * const migrate = defineMigration([\n * { type: 'addTable', name: 'sessions' },\n * { type: 'addIndex', table: 'users', field: 'email' },\n * { type: 'removeTable', name: 'legacyTokens' },\n * ]);\n *\n * const db = createIndexedDB({ name: 'app', version: 2, schema, migrate });\n * ```\n */\nexport function defineMigration(steps: MigrationStep[]): MigrationFn {\n return ({ db, tx }) => {\n for (const step of steps) {\n switch (step.type) {\n case 'addIndex': {\n const store = tx.objectStore(step.table);\n\n // keyPath mirrors the vault storage envelope: { value: T, expiresAt?: number }\n if (!store.indexNames.contains(step.field)) {\n store.createIndex(step.field, `value.${step.field}`);\n }\n\n break;\n }\n case 'addTable':\n if (!db.objectStoreNames.contains(step.name)) {\n db.createObjectStore(step.name);\n }\n\n break;\n case 'removeIndex': {\n const store = tx.objectStore(step.table);\n\n if (store.indexNames.contains(step.field)) {\n store.deleteIndex(step.field);\n }\n\n break;\n }\n case 'removeTable':\n if (db.objectStoreNames.contains(step.name)) {\n db.deleteObjectStore(step.name);\n }\n\n break;\n }\n }\n };\n}\n"],"mappings":";AA2BA,SAAgB,EAAgB,GAAqC;CACnE,QAAQ,EAAE,OAAI,YAAS;EACrB,KAAK,IAAM,KAAQ,GACjB,QAAQ,EAAK,MAAb;GACE,KAAK,YAAY;IACf,IAAM,IAAQ,EAAG,YAAY,EAAK,KAAK;IAGvC,AAAK,EAAM,WAAW,SAAS,EAAK,KAAK,KACvC,EAAM,YAAY,EAAK,OAAO,SAAS,EAAK,OAAO;IAGrD;GACF;GACA,KAAK;IACH,AAAK,EAAG,iBAAiB,SAAS,EAAK,IAAI,KACzC,EAAG,kBAAkB,EAAK,IAAI;IAGhC;GACF,KAAK,eAAe;IAClB,IAAM,IAAQ,EAAG,YAAY,EAAK,KAAK;IAEvC,AAAI,EAAM,WAAW,SAAS,EAAK,KAAK,KACtC,EAAM,YAAY,EAAK,KAAK;IAG9B;GACF;GACA,KAAK,eACH,AAAI,EAAG,iBAAiB,SAAS,EAAK,IAAI,KACxC,EAAG,kBAAkB,EAAK,IAAI;EAIpC;CAEJ;AACF"}
package/dist/prune.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_dev.cjs"),t=require("./errors.cjs");function n(n,r){if(!Number.isFinite(r.interval)||r.interval<=0)throw new t.VaultError(`scheduleExpiredPrune: interval must be a finite positive number`);let i=!0,a=()=>{i=!1,clearInterval(o)};r.signal?.addEventListener(`abort`,a,{once:!0});let o=setInterval(()=>{i&&n.pruneExpired().catch(n=>{n instanceof t.VaultDisposedError?(i=!1,clearInterval(o)):r.onError?r.onError(n):e.warn(`scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this. ${String(n)}`)})},r.interval);return a}exports.scheduleExpiredPrune=n;
1
+ const e=require("./errors.cjs");require("./_dev.cjs");function t(t,n){if(!Number.isFinite(n.interval)||n.interval<=0)throw new e.VaultError(`scheduleExpiredPrune: interval must be a finite positive number`);let r=!0,i=()=>{r=!1,clearInterval(a)};n.signal?.addEventListener(`abort`,i,{once:!0});let a=setInterval(()=>{r&&t.pruneExpired().catch(t=>{t instanceof e.VaultDisposedError?(r=!1,clearInterval(a)):n.onError?n.onError(t):`${String(t)}`})},n.interval);return i}exports.scheduleExpiredPrune=t;
2
2
  //# sourceMappingURL=prune.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"prune.cjs","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import type { Adapter, AnySchema } from './types';\n\nimport { warn } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\n/**\n * Schedules periodic calls to `adapter.pruneExpired()` using `setInterval`.\n * Returns a `stop` function to cancel the schedule.\n *\n * The schedule auto-cancels if the adapter is disposed — no need to call `stop()`\n * in a dispose handler when the adapter lifetime matches the schedule lifetime.\n *\n * ```ts\n * const stopPrune = scheduleExpiredPrune(db, { interval: ttl.hours(1) });\n * // later...\n * stopPrune();\n * ```\n */\nexport function scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<Adapter<S>, 'pruneExpired'>,\n options: {\n interval: number;\n /**\n * Called when `pruneExpired()` throws an error that is NOT a `VaultDisposedError`.\n * `VaultDisposedError` always stops the schedule automatically.\n * Without this callback, non-disposal errors are silently swallowed.\n */\n onError?: (err: unknown) => void;\n /**\n * When aborted, stops the schedule. Useful for tying the schedule lifetime\n * to an adapter's `disposalSignal`:\n * ```ts\n * scheduleExpiredPrune(db, { interval: ttl.hours(1), signal: db.disposalSignal });\n * ```\n */\n signal?: AbortSignal;\n },\n): () => void {\n if (!Number.isFinite(options.interval) || options.interval <= 0) {\n throw new VaultError('scheduleExpiredPrune: interval must be a finite positive number');\n }\n\n let active = true;\n\n const stop = (): void => {\n active = false;\n clearInterval(id);\n };\n\n options.signal?.addEventListener('abort', stop, { once: true });\n\n const id = setInterval(() => {\n if (!active) return;\n\n void adapter.pruneExpired().catch((err) => {\n if (err instanceof VaultDisposedError) {\n active = false;\n clearInterval(id);\n } else if (options.onError) {\n options.onError(err);\n } else {\n warn(`scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this. ${String(err)}`);\n }\n });\n }, options.interval);\n\n return stop;\n}\n"],"mappings":"wDAkBA,SAAgB,EACd,EACA,EAiBY,CACZ,GAAI,CAAC,OAAO,SAAS,EAAQ,QAAQ,GAAK,EAAQ,UAAY,EAC5D,MAAM,IAAI,EAAA,WAAW,iEAAiE,EAGxF,IAAI,EAAS,GAEP,MAAmB,CACvB,EAAS,GACT,cAAc,CAAE,CAClB,EAEA,EAAQ,QAAQ,iBAAiB,QAAS,EAAM,CAAE,KAAM,EAAK,CAAC,EAE9D,IAAM,EAAK,gBAAkB,CACtB,GAEL,EAAa,aAAa,CAAC,CAAC,MAAO,GAAQ,CACrC,aAAe,EAAA,oBACjB,EAAS,GACT,cAAc,CAAE,GACP,EAAQ,QACjB,EAAQ,QAAQ,CAAG,EAEnB,EAAA,KAAK,6EAA6E,OAAO,CAAG,GAAG,CAEnG,CAAC,CACH,EAAG,EAAQ,QAAQ,EAEnB,OAAO,CACT"}
1
+ {"version":3,"file":"prune.cjs","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import type { AnySchema, VaultStore } from './types';\n\nimport { warn } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\n/**\n * Schedules periodic calls to `adapter.pruneExpired()` using `setInterval`.\n * Returns a `stop` function to cancel the schedule.\n *\n * The schedule auto-cancels if the adapter is disposed — no need to call `stop()`\n * in a dispose handler when the adapter lifetime matches the schedule lifetime.\n *\n * ```ts\n * const stopPrune = scheduleExpiredPrune(db, { interval: ttl.hours(1) });\n * // later...\n * stopPrune();\n * ```\n */\nexport function scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<VaultStore<S>, 'pruneExpired'>,\n options: {\n interval: number;\n /**\n * Called when `pruneExpired()` throws an error that is NOT a `VaultDisposedError`.\n * `VaultDisposedError` always stops the schedule automatically.\n * Without this callback, non-disposal errors are silently swallowed.\n */\n onError?: (err: unknown) => void;\n /**\n * When aborted, stops the schedule. Useful for tying the schedule lifetime\n * to an adapter's `disposalSignal`:\n * ```ts\n * scheduleExpiredPrune(db, { interval: ttl.hours(1), signal: db.disposalSignal });\n * ```\n */\n signal?: AbortSignal;\n },\n): () => void {\n if (!Number.isFinite(options.interval) || options.interval <= 0) {\n throw new VaultError('scheduleExpiredPrune: interval must be a finite positive number');\n }\n\n let active = true;\n\n const stop = (): void => {\n active = false;\n clearInterval(id);\n };\n\n options.signal?.addEventListener('abort', stop, { once: true });\n\n const id = setInterval(() => {\n if (!active) return;\n\n void adapter.pruneExpired().catch((err) => {\n if (err instanceof VaultDisposedError) {\n active = false;\n clearInterval(id);\n } else if (options.onError) {\n options.onError(err);\n } else {\n warn(`scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this. ${String(err)}`);\n }\n });\n }, options.interval);\n\n return stop;\n}\n"],"mappings":"sDAkBA,SAAgB,EACd,EACA,EAiBY,CACZ,GAAI,CAAC,OAAO,SAAS,EAAQ,QAAQ,GAAK,EAAQ,UAAY,EAC5D,MAAM,IAAI,EAAA,WAAW,iEAAiE,EAGxF,IAAI,EAAS,GAEP,MAAmB,CACvB,EAAS,GACT,cAAc,CAAE,CAClB,EAEA,EAAQ,QAAQ,iBAAiB,QAAS,EAAM,CAAE,KAAM,EAAK,CAAC,EAE9D,IAAM,EAAK,gBAAkB,CACtB,GAEL,EAAa,aAAa,CAAC,CAAC,MAAO,GAAQ,CACrC,aAAe,EAAA,oBACjB,EAAS,GACT,cAAc,CAAE,GACP,EAAQ,QACjB,EAAQ,QAAQ,CAAG,EAEd,GAA6E,OAAO,CAAG,EAAvF,CAET,CAAC,CACH,EAAG,EAAQ,QAAQ,EAEnB,OAAO,CACT"}
package/dist/prune.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Adapter, AnySchema } from './types';
1
+ import type { AnySchema, VaultStore } from './types';
2
2
  /**
3
3
  * Schedules periodic calls to `adapter.pruneExpired()` using `setInterval`.
4
4
  * Returns a `stop` function to cancel the schedule.
@@ -12,7 +12,7 @@ import type { Adapter, AnySchema } from './types';
12
12
  * stopPrune();
13
13
  * ```
14
14
  */
15
- export declare function scheduleExpiredPrune<S extends AnySchema>(adapter: Pick<Adapter<S>, 'pruneExpired'>, options: {
15
+ export declare function scheduleExpiredPrune<S extends AnySchema>(adapter: Pick<VaultStore<S>, 'pruneExpired'>, options: {
16
16
  interval: number;
17
17
  /**
18
18
  * Called when `pruneExpired()` throws an error that is NOT a `VaultDisposedError`.
@@ -1 +1 @@
1
- {"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKlD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,SAAS,EACtD,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,EACzC,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GACA,MAAM,IAAI,CA8BZ"}
1
+ {"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAKrD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,SAAS,EACtD,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,EAC5C,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GACA,MAAM,IAAI,CA8BZ"}
package/dist/prune.js CHANGED
@@ -1,20 +1,20 @@
1
- import { warn as e } from "./_dev.js";
2
- import { VaultDisposedError as t, VaultError as n } from "./errors.js";
1
+ import { VaultDisposedError as e, VaultError as t } from "./errors.js";
2
+ import "./_dev.js";
3
3
  //#region src/prune.ts
4
- function r(r, i) {
5
- if (!Number.isFinite(i.interval) || i.interval <= 0) throw new n("scheduleExpiredPrune: interval must be a finite positive number");
6
- let a = !0, o = () => {
7
- a = !1, clearInterval(s);
4
+ function n(n, r) {
5
+ if (!Number.isFinite(r.interval) || r.interval <= 0) throw new t("scheduleExpiredPrune: interval must be a finite positive number");
6
+ let i = !0, a = () => {
7
+ i = !1, clearInterval(o);
8
8
  };
9
- i.signal?.addEventListener("abort", o, { once: !0 });
10
- let s = setInterval(() => {
11
- a && r.pruneExpired().catch((n) => {
12
- n instanceof t ? (a = !1, clearInterval(s)) : i.onError ? i.onError(n) : e(`scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this. ${String(n)}`);
9
+ r.signal?.addEventListener("abort", a, { once: !0 });
10
+ let o = setInterval(() => {
11
+ i && n.pruneExpired().catch((t) => {
12
+ t instanceof e ? (i = !1, clearInterval(o)) : r.onError ? r.onError(t) : `${String(t)}`;
13
13
  });
14
- }, i.interval);
15
- return o;
14
+ }, r.interval);
15
+ return a;
16
16
  }
17
17
  //#endregion
18
- export { r as scheduleExpiredPrune };
18
+ export { n as scheduleExpiredPrune };
19
19
 
20
20
  //# sourceMappingURL=prune.js.map
package/dist/prune.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"prune.js","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import type { Adapter, AnySchema } from './types';\n\nimport { warn } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\n/**\n * Schedules periodic calls to `adapter.pruneExpired()` using `setInterval`.\n * Returns a `stop` function to cancel the schedule.\n *\n * The schedule auto-cancels if the adapter is disposed — no need to call `stop()`\n * in a dispose handler when the adapter lifetime matches the schedule lifetime.\n *\n * ```ts\n * const stopPrune = scheduleExpiredPrune(db, { interval: ttl.hours(1) });\n * // later...\n * stopPrune();\n * ```\n */\nexport function scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<Adapter<S>, 'pruneExpired'>,\n options: {\n interval: number;\n /**\n * Called when `pruneExpired()` throws an error that is NOT a `VaultDisposedError`.\n * `VaultDisposedError` always stops the schedule automatically.\n * Without this callback, non-disposal errors are silently swallowed.\n */\n onError?: (err: unknown) => void;\n /**\n * When aborted, stops the schedule. Useful for tying the schedule lifetime\n * to an adapter's `disposalSignal`:\n * ```ts\n * scheduleExpiredPrune(db, { interval: ttl.hours(1), signal: db.disposalSignal });\n * ```\n */\n signal?: AbortSignal;\n },\n): () => void {\n if (!Number.isFinite(options.interval) || options.interval <= 0) {\n throw new VaultError('scheduleExpiredPrune: interval must be a finite positive number');\n }\n\n let active = true;\n\n const stop = (): void => {\n active = false;\n clearInterval(id);\n };\n\n options.signal?.addEventListener('abort', stop, { once: true });\n\n const id = setInterval(() => {\n if (!active) return;\n\n void adapter.pruneExpired().catch((err) => {\n if (err instanceof VaultDisposedError) {\n active = false;\n clearInterval(id);\n } else if (options.onError) {\n options.onError(err);\n } else {\n warn(`scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this. ${String(err)}`);\n }\n });\n }, options.interval);\n\n return stop;\n}\n"],"mappings":";;;AAkBA,SAAgB,EACd,GACA,GAiBY;CACZ,IAAI,CAAC,OAAO,SAAS,EAAQ,QAAQ,KAAK,EAAQ,YAAY,GAC5D,MAAM,IAAI,EAAW,iEAAiE;CAGxF,IAAI,IAAS,IAEP,UAAmB;EAEvB,AADA,IAAS,IACT,cAAc,CAAE;CAClB;CAEA,EAAQ,QAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC;CAE9D,IAAM,IAAK,kBAAkB;EACtB,KAEL,EAAa,aAAa,CAAC,CAAC,OAAO,MAAQ;GACzC,AAAI,aAAe,KACjB,IAAS,IACT,cAAc,CAAE,KACP,EAAQ,UACjB,EAAQ,QAAQ,CAAG,IAEnB,EAAK,6EAA6E,OAAO,CAAG,GAAG;EAEnG,CAAC;CACH,GAAG,EAAQ,QAAQ;CAEnB,OAAO;AACT"}
1
+ {"version":3,"file":"prune.js","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import type { AnySchema, VaultStore } from './types';\n\nimport { warn } from './_dev';\nimport { VaultDisposedError, VaultError } from './errors';\n\n/**\n * Schedules periodic calls to `adapter.pruneExpired()` using `setInterval`.\n * Returns a `stop` function to cancel the schedule.\n *\n * The schedule auto-cancels if the adapter is disposed — no need to call `stop()`\n * in a dispose handler when the adapter lifetime matches the schedule lifetime.\n *\n * ```ts\n * const stopPrune = scheduleExpiredPrune(db, { interval: ttl.hours(1) });\n * // later...\n * stopPrune();\n * ```\n */\nexport function scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<VaultStore<S>, 'pruneExpired'>,\n options: {\n interval: number;\n /**\n * Called when `pruneExpired()` throws an error that is NOT a `VaultDisposedError`.\n * `VaultDisposedError` always stops the schedule automatically.\n * Without this callback, non-disposal errors are silently swallowed.\n */\n onError?: (err: unknown) => void;\n /**\n * When aborted, stops the schedule. Useful for tying the schedule lifetime\n * to an adapter's `disposalSignal`:\n * ```ts\n * scheduleExpiredPrune(db, { interval: ttl.hours(1), signal: db.disposalSignal });\n * ```\n */\n signal?: AbortSignal;\n },\n): () => void {\n if (!Number.isFinite(options.interval) || options.interval <= 0) {\n throw new VaultError('scheduleExpiredPrune: interval must be a finite positive number');\n }\n\n let active = true;\n\n const stop = (): void => {\n active = false;\n clearInterval(id);\n };\n\n options.signal?.addEventListener('abort', stop, { once: true });\n\n const id = setInterval(() => {\n if (!active) return;\n\n void adapter.pruneExpired().catch((err) => {\n if (err instanceof VaultDisposedError) {\n active = false;\n clearInterval(id);\n } else if (options.onError) {\n options.onError(err);\n } else {\n warn(`scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this. ${String(err)}`);\n }\n });\n }, options.interval);\n\n return stop;\n}\n"],"mappings":";;;AAkBA,SAAgB,EACd,GACA,GAiBY;CACZ,IAAI,CAAC,OAAO,SAAS,EAAQ,QAAQ,KAAK,EAAQ,YAAY,GAC5D,MAAM,IAAI,EAAW,iEAAiE;CAGxF,IAAI,IAAS,IAEP,UAAmB;EAEvB,AADA,IAAS,IACT,cAAc,CAAE;CAClB;CAEA,EAAQ,QAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC;CAE9D,IAAM,IAAK,kBAAkB;EACtB,KAEL,EAAa,aAAa,CAAC,CAAC,OAAO,MAAQ;GACzC,AAAI,aAAe,KACjB,IAAS,IACT,cAAc,CAAE,KACP,EAAQ,UACjB,EAAQ,QAAQ,CAAG,IAEd,GAA6E,OAAO,CAAG,EAAvF;EAET,CAAC;CACH,GAAG,EAAQ,QAAQ;CAEnB,OAAO;AACT"}
package/dist/query.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs");async function t(e,t){let n=await e.source();for(let e of t)n=e.apply(n);return n}function n(t,n){if(!Number.isInteger(t)||t<0)throw new e.VaultError(`${n} must be a non-negative integer`);return t}function r(e,t){return{deleteMany:e.deleteMany,source:t}}function i(a,o=[]){let s=e=>i(a,[...o,e]);return{between(e,t,n){let c=String(e);if(o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({lower:t,type:`between`,upper:n})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{lower:t,type:`between`,upper:n})),o)}return s({apply:r=>r.filter(r=>{let i=r[e];return i>=t&&i<=n})})},count(){return t(a,o).then(e=>e.length)},async delete(){if(!a.deleteMany)throw new e.VaultError(`query.delete is not available for this adapter context`);let n=await t(a,o);return a.deleteMany(n)},equals(e,t){let n=String(e);if(o.length===0){if(a.getRange&&a.keyField===n)return i(r(a,()=>a.getRange({type:`eq`,value:t})));if(a.getIndexRange&&a.indexedFields?.has(n))return i(r(a,()=>a.getIndexRange(n,{type:`eq`,value:t})))}return i(a,[...o,{apply:n=>n.filter(n=>n[e]===t)}])},async exists(){return o.length===0?a.source().then(e=>e.length>0):t(a,o).then(e=>e.length>0)},filter(e){return s({apply:t=>t.filter(e)})},first(){return o.length===0?a.source().then(e=>e[0]):t(a,o).then(e=>e[0])},limit(e){let t=n(e,`query.limit`);return s({apply:e=>e.slice(0,t),isNonFilter:!0})},offset(e){let t=n(e,`query.offset`);return s({apply:e=>e.slice(t),isNonFilter:!0})},orderBy(e,t=`asc`){return s({apply:n=>{let r=t===`asc`?1:-1;return[...n].sort((t,n)=>{let i=t[e],a=n[e];return i===a?0:i>a?r:-r})},isNonFilter:!0})},startsWith(e,t,{ignoreCase:n=!1}={}){let c=String(e);if(!n&&t.length>0&&o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({prefix:t,type:`starts`})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{prefix:t,type:`starts`})),o)}let l=n?t.toLowerCase():t;return s({apply:t=>t.filter(t=>{let r=t[e];return typeof r==`string`?(n?r.toLowerCase():r).startsWith(l):!1})})},toArray(){return t(a,o)},totalCount(){return t(a,o.filter(e=>!e.isNonFilter)).then(e=>e.length)}}}exports.createQueryBuilder=i;
1
+ const e=require("./errors.cjs");async function t(e,t){let n=await e.source();for(let e of t)n=e.apply(n);return n}function n(t,n){if(!Number.isInteger(t)||t<0)throw new e.VaultError(`${n} must be a non-negative integer`);return t}function r(e,t){return{deleteMany:e.deleteMany,source:t}}function i(a,o=[]){let s=e=>i(a,[...o,e]);return{between(e,t,n){let c=String(e);if(o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({lower:t,type:`between`,upper:n})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{lower:t,type:`between`,upper:n})),o)}return s({apply:r=>r.filter(r=>{let i=r[e];return i>=t&&i<=n})})},count(){return t(a,o).then(e=>e.length)},async delete(){if(!a.deleteMany)throw new e.VaultError(`query.delete is not available for this adapter context`);let n=await t(a,o);return a.deleteMany(n)},equals(e,t){let n=String(e);if(o.length===0){if(a.getRange&&a.keyField===n)return i(r(a,()=>a.getRange({type:`eq`,value:t})));if(a.getIndexRange&&a.indexedFields?.has(n))return i(r(a,()=>a.getIndexRange(n,{type:`eq`,value:t})))}return i(a,[...o,{apply:n=>n.filter(n=>n[e]===t)}])},async exists(){return o.length===0?a.source().then(e=>e.length>0):t(a,o).then(e=>e.length>0)},filter(e){return s({apply:t=>t.filter(e)})},first(){return o.length===0?a.source().then(e=>e[0]):t(a,o).then(e=>e[0])},limit(e){let t=n(e,`query.limit`);return s({apply:e=>e.slice(0,t),isNonFilter:!0})},offset(e){let t=n(e,`query.offset`);return s({apply:e=>e.slice(t),isNonFilter:!0})},orderBy(e,t=`asc`){return s({apply:n=>{let r=t===`asc`?1:-1;return[...n].sort((t,n)=>{let i=t[e],a=n[e];return i===a?0:i>a?r:-r})},isNonFilter:!0})},startsWith(e,t,{ignoreCase:n=!1}={}){let c=String(e);if(!n&&t.length>0&&o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({prefix:t,type:`starts`})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{prefix:t,type:`starts`})),o)}let l=n?t.toLowerCase():t;return s({apply:t=>t.filter(t=>{let r=t[e];return typeof r==`string`&&(n?r.toLowerCase():r).startsWith(l)})})},toArray(){return t(a,o)},totalCount(){return t(a,o.filter(e=>!e.isNonFilter)).then(e=>e.length)}}}exports.createQueryBuilder=i;
2
2
  //# sourceMappingURL=query.cjs.map