@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.
- package/README.md +18 -59
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +3 -8
- package/dist/_dev.js.map +1 -1
- package/dist/adapter-core.cjs +1 -1
- package/dist/adapter-core.cjs.map +1 -1
- package/dist/adapter-core.d.ts +6 -3
- package/dist/adapter-core.d.ts.map +1 -1
- package/dist/adapter-core.js +138 -148
- package/dist/adapter-core.js.map +1 -1
- package/dist/adapters/indexeddb.cjs +1 -1
- package/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.ts +2 -5
- package/dist/adapters/indexeddb.d.ts.map +1 -1
- package/dist/adapters/indexeddb.js +113 -127
- package/dist/adapters/indexeddb.js.map +1 -1
- package/dist/adapters/memory.cjs +1 -1
- package/dist/adapters/memory.cjs.map +1 -1
- package/dist/adapters/memory.d.ts +3 -9
- package/dist/adapters/memory.d.ts.map +1 -1
- package/dist/adapters/memory.js +104 -140
- package/dist/adapters/memory.js.map +1 -1
- package/dist/adapters/webstorage.cjs +1 -1
- package/dist/adapters/webstorage.cjs.map +1 -1
- package/dist/adapters/webstorage.d.ts +3 -3
- package/dist/adapters/webstorage.d.ts.map +1 -1
- package/dist/adapters/webstorage.js +72 -67
- package/dist/adapters/webstorage.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -10
- package/dist/internal.cjs +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.ts +8 -37
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +48 -98
- package/dist/internal.js.map +1 -1
- package/dist/migration.cjs +1 -1
- package/dist/migration.cjs.map +1 -1
- package/dist/migration.js +1 -3
- package/dist/migration.js.map +1 -1
- package/dist/prune.cjs +1 -1
- package/dist/prune.cjs.map +1 -1
- package/dist/prune.d.ts +2 -2
- package/dist/prune.d.ts.map +1 -1
- package/dist/prune.js +13 -13
- package/dist/prune.js.map +1 -1
- package/dist/query.cjs +1 -1
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.ts +6 -6
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +1 -1
- package/dist/query.js.map +1 -1
- package/dist/streaming.d.ts +1 -21
- package/dist/streaming.d.ts.map +1 -1
- package/dist/ttl.cjs +1 -1
- package/dist/ttl.cjs.map +1 -1
- package/dist/ttl.d.ts +2 -47
- package/dist/ttl.d.ts.map +1 -1
- package/dist/ttl.js +1 -8
- package/dist/ttl.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +29 -316
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault.cjs +1 -1
- package/dist/vault.cjs.map +1 -1
- package/dist/vault.iife.js +1 -1
- package/dist/vault.iife.js.map +1 -1
- package/dist/vault.js +1 -1
- package/dist/vault.js.map +1 -1
- package/dist/versioned-codec.d.ts +1 -43
- package/dist/versioned-codec.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/streaming.cjs +0 -2
- package/dist/streaming.cjs.map +0 -1
- package/dist/streaming.js +0 -21
- package/dist/streaming.js.map +0 -1
- package/dist/versioned-codec.cjs +0 -2
- package/dist/versioned-codec.cjs.map +0 -1
- package/dist/versioned-codec.js +0 -32
- package/dist/versioned-codec.js.map +0 -1
package/dist/adapter-core.js
CHANGED
|
@@ -1,85 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { assertTtlMs as l } from "./ttl.js";
|
|
1
|
+
import { VaultDisposedError as e, VaultError as t, VaultScopeError as n } from "./errors.js";
|
|
2
|
+
import { createObserverHub as r, getRecordKey as i } from "./internal.js";
|
|
3
|
+
import { createQueryBuilder as a } from "./query.js";
|
|
4
|
+
import { assertTtlMs as o } from "./ttl.js";
|
|
6
5
|
//#region src/adapter-core.ts
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
return n !== void 0 &&
|
|
6
|
+
var s = typeof performance < "u" ? () => performance.now() : () => Date.now();
|
|
7
|
+
function c(e, t, n) {
|
|
8
|
+
return n !== void 0 && o(n, "put/putAll"), n ?? e[t].defaultTtl;
|
|
10
9
|
}
|
|
11
|
-
function
|
|
12
|
-
let
|
|
13
|
-
if (
|
|
10
|
+
function l(e, n, r, a, o) {
|
|
11
|
+
let s = i(e, n, a);
|
|
12
|
+
if (s !== r) throw new t(`${o}: key field "${e[n].key}" must be "${String(r)}" but got "${String(s)}" in table "${n}"`);
|
|
14
13
|
}
|
|
15
|
-
function
|
|
14
|
+
function u(e, t, n) {
|
|
16
15
|
return e.getMany ? e.getMany(t, n) : Promise.all(n.map((n) => e.get(t, n)));
|
|
17
16
|
}
|
|
18
|
-
function
|
|
19
|
-
let
|
|
17
|
+
function d(e, t, n, r) {
|
|
18
|
+
let a = n[e], o = a.key, s = a.indexes ? new Set(a.indexes) : void 0;
|
|
20
19
|
return {
|
|
21
|
-
deleteMany: async (
|
|
22
|
-
if (
|
|
23
|
-
let
|
|
24
|
-
return
|
|
20
|
+
deleteMany: async (a) => {
|
|
21
|
+
if (a.length === 0) return 0;
|
|
22
|
+
let o = a.map((t) => i(n, e, t)), s = await t.deleteMany(e, o);
|
|
23
|
+
return s > 0 && r(e), s;
|
|
25
24
|
},
|
|
26
25
|
getIndexRange: t.getByIndexRange ? (n, r) => t.getByIndexRange(e, n, r) : void 0,
|
|
27
26
|
getRange: t.getByKeyRange ? (n) => t.getByKeyRange(e, n) : void 0,
|
|
28
|
-
indexedFields:
|
|
29
|
-
keyField:
|
|
27
|
+
indexedFields: s,
|
|
28
|
+
keyField: o,
|
|
30
29
|
source: () => t.getAll(e)
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
|
-
function
|
|
34
|
-
let
|
|
35
|
-
if (!
|
|
32
|
+
function f(e, t, r, i, o) {
|
|
33
|
+
let s = i ?? ((e, t) => t), f = o ? (e) => {
|
|
34
|
+
if (!o.has(e)) throw new n(`table "${e}" is not part of this batch scope`);
|
|
36
35
|
} : (e) => {};
|
|
37
36
|
return {
|
|
38
37
|
async clear(e) {
|
|
39
|
-
|
|
38
|
+
f(e);
|
|
39
|
+
let n = await t.count(e);
|
|
40
|
+
await t.clear(e), n > 0 && r(e);
|
|
40
41
|
},
|
|
41
42
|
async count(e) {
|
|
42
|
-
return
|
|
43
|
+
return f(e), t.count(e);
|
|
43
44
|
},
|
|
44
|
-
async delete(e,
|
|
45
|
-
|
|
46
|
-
let i = await t.delete(e,
|
|
47
|
-
return i &&
|
|
45
|
+
async delete(e, n) {
|
|
46
|
+
f(e);
|
|
47
|
+
let i = await t.delete(e, n);
|
|
48
|
+
return i && r(e), i;
|
|
48
49
|
},
|
|
49
|
-
async deleteMany(e,
|
|
50
|
-
|
|
51
|
-
let i = await t.deleteMany(e,
|
|
52
|
-
return i > 0 &&
|
|
50
|
+
async deleteMany(e, n) {
|
|
51
|
+
f(e);
|
|
52
|
+
let i = await t.deleteMany(e, n);
|
|
53
|
+
return i > 0 && r(e), i;
|
|
53
54
|
},
|
|
54
55
|
async entries(n) {
|
|
55
|
-
|
|
56
|
+
f(n);
|
|
56
57
|
let r = await t.getAll(n), i = e[n].key;
|
|
57
58
|
return r.map((e) => [e[i], e]);
|
|
58
59
|
},
|
|
59
60
|
async get(e, n) {
|
|
60
|
-
return
|
|
61
|
+
return f(e), t.get(e, n);
|
|
61
62
|
},
|
|
62
63
|
async getAll(e) {
|
|
63
|
-
return
|
|
64
|
+
return f(e), t.getAll(e);
|
|
64
65
|
},
|
|
65
66
|
async getMany(e, n) {
|
|
66
|
-
return
|
|
67
|
+
return f(e), u(t, e, n);
|
|
67
68
|
},
|
|
68
|
-
async getOrDefault(
|
|
69
|
-
|
|
70
|
-
let
|
|
71
|
-
if (
|
|
72
|
-
let
|
|
73
|
-
return
|
|
69
|
+
async getOrDefault(n, i, a, o) {
|
|
70
|
+
f(n);
|
|
71
|
+
let u = await t.get(n, i);
|
|
72
|
+
if (u !== void 0) return u;
|
|
73
|
+
let d = a();
|
|
74
|
+
return l(e, n, i, d, "getOrDefault: defaultFn()"), await t.put(n, s(n, d), c(e, n, o)), r(n), d;
|
|
74
75
|
},
|
|
75
76
|
async has(e, n) {
|
|
76
|
-
return
|
|
77
|
+
return f(e), t.has(e, n);
|
|
77
78
|
},
|
|
78
79
|
async isEmpty(e) {
|
|
79
|
-
return
|
|
80
|
+
return f(e), await t.count(e) === 0;
|
|
80
81
|
},
|
|
81
82
|
async keys(n, r) {
|
|
82
|
-
if (
|
|
83
|
+
if (f(n), r) {
|
|
83
84
|
let i = await t.getAll(n), a = e[n].key;
|
|
84
85
|
return i.filter(r).map((e) => e[a]);
|
|
85
86
|
}
|
|
@@ -87,89 +88,79 @@ function h(e, t, n, i, a) {
|
|
|
87
88
|
let i = await t.getAll(n), a = e[n].key;
|
|
88
89
|
return i.map((e) => e[a]);
|
|
89
90
|
},
|
|
90
|
-
async put(
|
|
91
|
-
|
|
91
|
+
async put(n, i, a) {
|
|
92
|
+
f(n), await t.put(n, s(n, i), c(e, n, a)), r(n);
|
|
92
93
|
},
|
|
93
|
-
async putAll(
|
|
94
|
-
if (
|
|
95
|
-
let
|
|
96
|
-
await t.putAll(
|
|
94
|
+
async putAll(n, i, a) {
|
|
95
|
+
if (f(n), i.length === 0) return;
|
|
96
|
+
let o = i.map((e) => s(n, e));
|
|
97
|
+
await t.putAll(n, o, c(e, n, a)), r(n);
|
|
97
98
|
},
|
|
98
|
-
query(
|
|
99
|
-
return
|
|
99
|
+
query(n) {
|
|
100
|
+
return f(n), a(d(n, t, e, r));
|
|
100
101
|
},
|
|
101
|
-
async update(
|
|
102
|
-
|
|
103
|
-
let
|
|
104
|
-
if (!
|
|
105
|
-
let
|
|
106
|
-
...
|
|
102
|
+
async update(n, i, a, o) {
|
|
103
|
+
f(n);
|
|
104
|
+
let u = await t.get(n, i);
|
|
105
|
+
if (!u) return;
|
|
106
|
+
let d = {
|
|
107
|
+
...u,
|
|
107
108
|
...a
|
|
108
109
|
};
|
|
109
|
-
return
|
|
110
|
+
return l(e, n, i, d, "update"), await t.put(n, s(n, d), c(e, n, o)), r(n), d;
|
|
110
111
|
},
|
|
111
|
-
async upsert(
|
|
112
|
-
|
|
113
|
-
let
|
|
114
|
-
return
|
|
112
|
+
async upsert(n, i, a, o) {
|
|
113
|
+
f(n);
|
|
114
|
+
let u = a(await t.get(n, i));
|
|
115
|
+
return l(e, n, i, u, "upsert: fn()"), await t.put(n, s(n, u), c(e, n, o)), r(n), u;
|
|
115
116
|
}
|
|
116
117
|
};
|
|
117
118
|
}
|
|
118
|
-
function
|
|
119
|
-
let { logger:
|
|
120
|
-
if (
|
|
121
|
-
let t = await
|
|
122
|
-
return
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
131
|
-
let E = (e, t) => {
|
|
132
|
-
let r = _?.[e];
|
|
133
|
-
if (!r) return t;
|
|
119
|
+
function p(n, i, o) {
|
|
120
|
+
let { logger: c, onMetrics: l, validators: u } = o ?? {}, p = r((e) => i.getAll(e), c ? (e) => c.error(e instanceof Error ? e : Error(String(e)), "[vault] observer notification failed") : void 0), m = /* @__PURE__ */ new Map(), h = async (e) => {
|
|
121
|
+
if (m.has(e)) return m.get(e);
|
|
122
|
+
let t = await i.count(e);
|
|
123
|
+
return m.set(e, t), t;
|
|
124
|
+
}, g = (e) => {
|
|
125
|
+
m.delete(e), p.notify(e), o?.onMutation?.(e);
|
|
126
|
+
}, _ = (e) => {
|
|
127
|
+
m.delete(e), p.notify(e);
|
|
128
|
+
}, v = i.pruneAllExpired?.bind(i), y = i.getRawCount?.bind(i), b = o?.onCrossTabMessage?.(_) ?? void 0, x = (e, n) => {
|
|
129
|
+
let r = u?.[e];
|
|
130
|
+
if (!r) return n;
|
|
134
131
|
try {
|
|
135
|
-
return r.parse(
|
|
136
|
-
} catch (
|
|
137
|
-
throw new
|
|
132
|
+
return r.parse(n);
|
|
133
|
+
} catch (n) {
|
|
134
|
+
throw new t(`validation failed for table "${e}"`, { cause: n });
|
|
138
135
|
}
|
|
139
|
-
},
|
|
140
|
-
if (!
|
|
141
|
-
let r =
|
|
142
|
-
return n().finally(() =>
|
|
143
|
-
duration:
|
|
136
|
+
}, S = (e, t, n) => {
|
|
137
|
+
if (!l) return n();
|
|
138
|
+
let r = s();
|
|
139
|
+
return n().finally(() => l({
|
|
140
|
+
duration: s() - r,
|
|
144
141
|
operation: t,
|
|
145
142
|
table: e
|
|
146
143
|
}));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
if (P) throw new t();
|
|
156
|
-
}, I = {
|
|
157
|
-
async batch(t, n) {
|
|
158
|
-
if (F(), t.length === 0) throw new r("batch requires at least one table");
|
|
159
|
-
return !k && !A && (A = !0, e("batch() on this adapter is not atomic — concurrent mutations may interleave. Use createIndexedDB for atomic batches.")), D("*", "batch", () => k ? k(t, n) : O(n, new Set(t)));
|
|
160
|
-
},
|
|
144
|
+
};
|
|
145
|
+
o?.onTransactions?.({
|
|
146
|
+
notifyMutation: g,
|
|
147
|
+
validate: x
|
|
148
|
+
});
|
|
149
|
+
let C = f(n, i, g, x), w = new AbortController(), T = !1, E = () => {
|
|
150
|
+
if (T) throw new e();
|
|
151
|
+
}, D = {
|
|
161
152
|
async clear(e) {
|
|
162
|
-
|
|
153
|
+
E(), await S(e, "clear", () => C.clear(e)), m.set(e, 0);
|
|
163
154
|
},
|
|
164
155
|
async count(e) {
|
|
165
|
-
return
|
|
156
|
+
return E(), S(e, "count", () => h(e));
|
|
166
157
|
},
|
|
167
158
|
async debug() {
|
|
168
|
-
|
|
169
|
-
let e = Object.keys(
|
|
159
|
+
E();
|
|
160
|
+
let e = Object.keys(n);
|
|
170
161
|
return { tables: await Promise.all(e.map(async (e) => {
|
|
171
|
-
let t =
|
|
172
|
-
return
|
|
162
|
+
let t = y ? await y(e) : void 0, n = await i.count(e);
|
|
163
|
+
return m.set(e, n), {
|
|
173
164
|
expiredCount: t === void 0 ? 0 : Math.max(0, t - n),
|
|
174
165
|
name: e,
|
|
175
166
|
recordCount: n
|
|
@@ -177,93 +168,92 @@ function g(s, l, d) {
|
|
|
177
168
|
})) };
|
|
178
169
|
},
|
|
179
170
|
async delete(e, t) {
|
|
180
|
-
return
|
|
171
|
+
return E(), S(e, "delete", () => C.delete(e, t));
|
|
181
172
|
},
|
|
182
173
|
async deleteMany(e, t) {
|
|
183
|
-
return
|
|
174
|
+
return E(), S(e, "deleteMany", () => C.deleteMany(e, t));
|
|
184
175
|
},
|
|
185
176
|
get disposalSignal() {
|
|
186
|
-
return
|
|
177
|
+
return w.signal;
|
|
187
178
|
},
|
|
188
179
|
async dispose() {
|
|
189
|
-
|
|
180
|
+
T || (T = !0, w.abort(), b?.(), p.dispose(), await i.dispose?.());
|
|
190
181
|
},
|
|
191
182
|
get disposed() {
|
|
192
|
-
return
|
|
183
|
+
return T;
|
|
193
184
|
},
|
|
194
185
|
async entries(e) {
|
|
195
|
-
return
|
|
186
|
+
return E(), S(e, "entries", () => C.entries(e));
|
|
196
187
|
},
|
|
197
188
|
async get(e, t) {
|
|
198
|
-
return
|
|
189
|
+
return E(), S(e, "get", () => C.get(e, t));
|
|
199
190
|
},
|
|
200
191
|
async getAll(e) {
|
|
201
|
-
return
|
|
192
|
+
return E(), S(e, "getAll", () => C.getAll(e));
|
|
202
193
|
},
|
|
203
194
|
async getMany(e, t) {
|
|
204
|
-
return
|
|
195
|
+
return E(), S(e, "getMany", () => C.getMany(e, t));
|
|
205
196
|
},
|
|
206
197
|
async getOrDefault(e, t, n, r) {
|
|
207
|
-
return
|
|
198
|
+
return E(), S(e, "getOrDefault", () => C.getOrDefault(e, t, n, r));
|
|
208
199
|
},
|
|
209
200
|
async has(e, t) {
|
|
210
|
-
return
|
|
201
|
+
return E(), S(e, "has", () => C.has(e, t));
|
|
211
202
|
},
|
|
212
203
|
async isEmpty(e) {
|
|
213
|
-
return
|
|
204
|
+
return E(), S(e, "isEmpty", async () => await h(e) === 0);
|
|
214
205
|
},
|
|
215
206
|
async keys(e, t) {
|
|
216
|
-
return
|
|
207
|
+
return E(), S(e, "keys", () => C.keys(e, t));
|
|
217
208
|
},
|
|
218
209
|
observe(e, t, n) {
|
|
219
|
-
return
|
|
210
|
+
return E(), p.observe(e, t, n);
|
|
220
211
|
},
|
|
221
|
-
observeMany: (e, t, n) => (F(), M(e, t, n)),
|
|
222
212
|
async pruneExpired(e) {
|
|
223
|
-
|
|
224
|
-
let t = e ?? Object.keys(
|
|
225
|
-
if (!e &&
|
|
226
|
-
let e = await
|
|
227
|
-
for (let [t, n] of Object.entries(e)) n > 0 &&
|
|
213
|
+
E();
|
|
214
|
+
let t = e ?? Object.keys(n);
|
|
215
|
+
if (!e && v) {
|
|
216
|
+
let e = await v();
|
|
217
|
+
for (let [t, n] of Object.entries(e)) n > 0 && m.delete(t);
|
|
228
218
|
return e;
|
|
229
219
|
}
|
|
230
|
-
let
|
|
231
|
-
let t = await
|
|
232
|
-
return t > 0 &&
|
|
220
|
+
let r = await Promise.all(t.map(async (e) => {
|
|
221
|
+
let t = await i.pruneExpiredInTable(e);
|
|
222
|
+
return t > 0 && m.delete(e), [e, t];
|
|
233
223
|
}));
|
|
234
|
-
return Object.fromEntries(
|
|
224
|
+
return Object.fromEntries(r);
|
|
235
225
|
},
|
|
236
226
|
async put(e, t, n) {
|
|
237
|
-
|
|
227
|
+
E(), await S(e, "put", () => C.put(e, t, n));
|
|
238
228
|
},
|
|
239
229
|
async putAll(e, t, n) {
|
|
240
|
-
|
|
230
|
+
E(), await S(e, "putAll", () => C.putAll(e, t, n));
|
|
241
231
|
},
|
|
242
232
|
query(e) {
|
|
243
|
-
|
|
244
|
-
let t =
|
|
245
|
-
return
|
|
233
|
+
E();
|
|
234
|
+
let t = d(e, i, n, g);
|
|
235
|
+
return a(l ? {
|
|
246
236
|
...t,
|
|
247
|
-
deleteMany: t.deleteMany ? (n) =>
|
|
248
|
-
source: () =>
|
|
237
|
+
deleteMany: t.deleteMany ? (n) => S(e, "queryDelete", () => t.deleteMany(n)) : void 0,
|
|
238
|
+
source: () => S(e, "query", () => i.getAll(e))
|
|
249
239
|
} : t);
|
|
250
240
|
},
|
|
251
241
|
async [Symbol.asyncDispose]() {
|
|
252
|
-
await
|
|
242
|
+
await D.dispose();
|
|
253
243
|
},
|
|
254
244
|
async update(e, t, n, r) {
|
|
255
|
-
return
|
|
245
|
+
return E(), S(e, "update", () => C.update(e, t, n, r));
|
|
256
246
|
},
|
|
257
247
|
async upsert(e, t, n, r) {
|
|
258
|
-
return
|
|
259
|
-
},
|
|
260
|
-
watch(e, t) {
|
|
261
|
-
return F(), o((t) => v.observe(e, t), t?.mode ?? "latest", t?.signal);
|
|
248
|
+
return E(), S(e, "upsert", () => C.upsert(e, t, n, r));
|
|
262
249
|
}
|
|
263
250
|
};
|
|
264
|
-
return
|
|
251
|
+
return D;
|
|
252
|
+
}
|
|
253
|
+
function m(e, t) {
|
|
254
|
+
return Object.assign(e, { batch: t });
|
|
265
255
|
}
|
|
266
256
|
//#endregion
|
|
267
|
-
export {
|
|
257
|
+
export { p as buildAdapterOps, f as buildTxContext, m as withIndexedDbTransactions };
|
|
268
258
|
|
|
269
259
|
//# sourceMappingURL=adapter-core.js.map
|
package/dist/adapter-core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-core.js","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":"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"}
|
|
1
|
+
{"version":3,"file":"adapter-core.js","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,2 +1,2 @@
|
|
|
1
|
-
const e=require("../errors.cjs"),t=require("../internal.cjs"),n=require("../ttl.cjs"),r=require("../adapter-core.cjs");function i(t){return new Promise((n,r)=>{t.onsuccess=()=>n(t.result),t.onerror=()=>r(t.error??new e.VaultError(`IndexedDB request failed`))})}function a(t,n,r){return new e.VaultError(`${n} on "${t}"${
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../internal.cjs"),n=require("../ttl.cjs"),r=require("../adapter-core.cjs");function i(t){return new Promise((n,r)=>{t.onsuccess=()=>n(t.result),t.onerror=()=>r(t.error??new e.VaultError(`IndexedDB request failed`))})}function a(t,n,r){let i=r instanceof Error&&r.message?`: ${r.message}`:``;return new e.VaultError(`${n} on "${t}"${i}`,{cause:r})}function o(e,t,n){return new Promise((r,i)=>{let o,s;Promise.resolve().then(n).then(e=>{o=e}).catch(t=>{s=t;try{e.abort()}catch{}});let c=(e,n=`transaction failed`)=>{s instanceof Error?i(s):i(a(t,n,s??e))};e.oncomplete=()=>{if(s){c(void 0);return}r(o)},e.onerror=()=>i(a(t,`transaction error`,e.error)),e.onabort=()=>c(e.error,`transaction aborted`)})}async function s(e,t){let n=await i(e.getAll()),r=[];for(let e of n){let n=t(e);n!==void 0&&r.push(n)}return r}async function c(e,t,n,r){let a=e.index(t),o=n.type===`eq`?IDBKeyRange.only(n.value):n.type===`between`?IDBKeyRange.bound(n.lower,n.upper):IDBKeyRange.bound(n.prefix,n.prefix+``),s=await i(a.getAll(o)),c=[];for(let e of s){let t=r(e);t!==void 0&&c.push(t)}return c}async function l(e,t,n){let r=await i(e.get(t));if(r!=null)return n(r)}async function u(e,t,n){return await l(e,t,n)!==void 0}async function d(e,t,n){let r=await u(e,t,n);return await i(e.delete(t)),r}async function f(e,t,n){return t.length===0?0:(await Promise.all(t.map(t=>d(e,t,n)))).filter(Boolean).length}async function p(e,t,n,r,a){await i(e.put(r(n,a),t))}function m(t){return new Promise((r,i)=>{let a=0,o=t.openCursor();o.onerror=()=>i(o.error??new e.VaultError(`IndexedDB cursor failed during prune`)),o.onsuccess=()=>{let e=o.result;if(!e){r(a);return}let t=n.parseStored(e.value);(!t||n.isExpired(t.expiresAt))&&(e.delete(),a+=1),e.continue()}})}function h(t,n){return{[Symbol.asyncIterator](){let r=t.openCursor(),i={type:`idle`},a=e=>{if(i.type===`waiting`){let{resolve:t}=i;i=e.done?{type:`done`}:{type:`idle`},t(e)}else i={result:e,type:`buffered`}};return r.onerror=()=>{let t=r.error??new e.VaultError(`IndexedDB cursor iteration failed`);if(i.type===`waiting`){let{reject:e}=i;i={type:`done`},e(t)}else i={error:t,type:`error`}},r.onsuccess=()=>{let e=r.result;if(!e){a({done:!0,value:void 0});return}let t=n(e.value);e.continue(),t!==void 0&&a({done:!1,value:t})},{next(){if(i.type===`error`){let{error:e}=i;return i={type:`done`},Promise.reject(e)}if(i.type===`buffered`){let{result:e}=i;return i=e.done?{type:`done`}:{type:`idle`},Promise.resolve(e)}return i.type===`done`?Promise.resolve({done:!0,value:void 0}):new Promise((e,t)=>{i={reject:t,resolve:e,type:`waiting`}})},return(e){return i.type===`waiting`&&i.resolve({done:!0,value:void 0}),i={type:`done`},Promise.resolve({done:!0,value:e})},throw(e){return i.type===`waiting`&&i.reject(e),i={type:`done`},Promise.reject(e)}}}}}function g(e,n,r,a){let o=e=>n.objectStore(e);return{clear:async e=>{await i(o(e).clear())},count:async e=>(await i(o(e).getAll())).filter(e=>r(e)!==void 0).length,delete:(e,n)=>d(o(e),t.encodeVaultKey(n),r),deleteMany:(e,n)=>f(o(e),n.map(t.encodeVaultKey),r),get:(e,n)=>l(o(e),t.encodeVaultKey(n),r),getAll:e=>s(o(e),r),getByIndexRange:(e,t,n)=>c(o(e),t,n,r),getMany:(e,n)=>Promise.all(n.map(n=>l(o(e),t.encodeVaultKey(n),r))),has:(e,n)=>u(o(e),t.encodeVaultKey(n),r),pruneExpiredInTable:e=>m(o(e)),put(n,r,i){return p(o(n),t.encodeVaultKey(t.getRecordKey(e,n,r)),r,a,i)},putAll(n,r,i){return Promise.all(r.map(r=>p(o(n),t.encodeVaultKey(t.getRecordKey(e,n,r)),r,a,i))).then(()=>void 0)}}}function _(a){let{logger:_,migrate:v,name:y,onMetrics:b,schema:x,validators:S,version:C=1}=a;if(!Number.isInteger(C)||C<1)throw new e.VaultError(`createIndexedDB: version must be a positive integer, got ${String(C)}`);let w=e=>{let t=n.parseStored(e);return!t||n.isExpired(t.expiresAt)?void 0:t.value},T=(e,t)=>t===void 0?{value:e}:{expiresAt:Date.now()+t,value:e},E=typeof BroadcastChannel<`u`?new BroadcastChannel(`vault:${y}`):void 0;!E&&_&&_.error(`[vault] BroadcastChannel unavailable — cross-tab sync disabled for "${y}"`);let D=null,O=null,k=!1,A=(e,t)=>{for(let[n,r]of Object.entries(x)){let i;i=e.objectStoreNames.contains(n)?t.objectStore(n):e.createObjectStore(n);let a=r.indexes??[];for(let e of a)i.indexNames.contains(e)||i.createIndex(e,`value.${e}`)}},j=async()=>(O||=new Promise((t,n)=>{let r=indexedDB.open(y,C);r.onupgradeneeded=t=>{let i=r.result,a=r.transaction;if(v)try{v({db:i,newVersion:t.newVersion??null,oldVersion:t.oldVersion,tx:a})}catch(t){try{a.abort()}catch{}n(new e.VaultMigrationError(`migration failed for "${y}"`,{cause:t}));return}A(i,a)},r.onsuccess=()=>{if(k){r.result.close(),t();return}let e=r.result;e.onversionchange=()=>{e.close(),D=null,O=null},D=e,t()},r.onerror=()=>{O=null,n(new e.VaultError(`failed to open "${y}"`,{cause:r.error}))}}),O),M=async(t,n,r)=>{if(k||(D||await j(),!D))throw new e.VaultDisposedError(`"${y}" is disposed`);let i=String(t),a=D.transaction(i,n);return o(a,`${y}/${i}`,()=>r(a.objectStore(i)))},N=async()=>{if(k||(D||await j(),!D))throw new e.VaultDisposedError(`"${y}" is disposed`);return D},P=e=>{E?.postMessage({table:String(e)})},F={clear:e=>M(e,`readwrite`,e=>i(e.clear()).then(()=>void 0)),count:e=>M(e,`readonly`,async e=>(await i(e.getAll())).filter(e=>w(e)!==void 0).length),delete:(e,n)=>M(e,`readwrite`,e=>d(e,t.encodeVaultKey(n),w)),deleteMany:(e,n)=>n.length===0?Promise.resolve(0):M(e,`readwrite`,e=>f(e,n.map(t.encodeVaultKey),w)),async dispose(){k=!0,E?.close(),O&&await O.catch(()=>{}),D?.close(),D=null,O=null},get:(e,n)=>M(e,`readonly`,e=>l(e,t.encodeVaultKey(n),w)),getAll:e=>M(e,`readonly`,e=>s(e,w)),getAllKeys:e=>M(e,`readonly`,async t=>{let n=await s(t,w),r=x[e].key;return n.map(e=>e[r])}),getByIndexRange:(e,t,n)=>M(e,`readonly`,e=>c(e,t,n,w)),getMany:(e,n)=>n.length===0?Promise.resolve([]):M(e,`readonly`,e=>Promise.all(n.map(n=>l(e,t.encodeVaultKey(n),w)))),getRawCount:e=>M(e,`readonly`,e=>i(e.count())),has:(e,n)=>M(e,`readonly`,e=>u(e,t.encodeVaultKey(n),w)),async pruneAllExpired(){let e=await N(),t=Object.keys(x),n=e.transaction(t,`readwrite`),r=await o(n,`${y}/pruneAll`,()=>Promise.all(t.map(async e=>[e,await m(n.objectStore(e))])));return Object.fromEntries(r)},pruneExpiredInTable:e=>M(e,`readwrite`,e=>m(e)),put(e,n,r){let i=t.encodeVaultKey(t.getRecordKey(x,e,n));return M(e,`readwrite`,e=>p(e,i,n,T,r))},putAll(e,n,r){return M(e,`readwrite`,i=>Promise.all(n.map(n=>p(i,t.encodeVaultKey(t.getRecordKey(x,e,n)),n,T,r))).then(()=>void 0))}},I=async(e,t,n,i)=>{let a=(await N()).transaction([...e],`readwrite`),s=new Set,c=g(x,a,w,T),l=new Set(e),u=r.buildTxContext(x,c,e=>s.add(e),i,l),d=await o(a,y,()=>t(u));for(let e of s)n(e);return d},L,R={...r.buildAdapterOps(x,F,{logger:_,onCrossTabMessage(e){if(E)return E.onmessage=t=>{let n=t.data?.table;!n||!Object.hasOwn(x,n)||e(n)},()=>{E.onmessage=null}},onMetrics:b,onMutation:P,onTransactions:e=>{L=(t,n)=>I(t,n,e.notifyMutation,e.validate)},schema:x,validators:S}),iterate(t){if(k)throw new e.VaultDisposedError(`"${y}" is disposed`);let n=async()=>{if(D||await j(),!D||k)throw new e.VaultDisposedError(`"${y}" is disposed`);return h(D.transaction(String(t),`readonly`).objectStore(String(t)),w)},r,i=()=>n().then(e=>(r=e[Symbol.asyncIterator](),r));return{[Symbol.asyncIterator](){return{next(){return r?r.next():i().then(e=>e.next())},return(e){return r?r.return?.(e)??Promise.resolve({done:!0,value:e}):Promise.resolve({done:!0,value:e})},throw(e){return r?r.throw?.(e)??Promise.reject(e):Promise.reject(e)}}}}}};if(!L)throw new e.VaultError(`IndexedDB transaction capability was not initialized`);return r.withIndexedDbTransactions(R,L)}exports.createIndexedDB=_;
|
|
2
2
|
//# sourceMappingURL=indexeddb.cjs.map
|