@gonvex/client 0.1.31 → 0.1.32
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/dist/index.d.ts +5 -0
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/kv-stores.d.ts +61 -0
- package/dist/kv-stores.js +447 -0
- package/dist/kv-stores.js.map +1 -0
- package/dist/outbox.d.ts +58 -0
- package/dist/outbox.js +180 -0
- package/dist/outbox.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { OutboxStore } from "./outbox.js";
|
|
2
|
+
import { type QueryCacheStore } from "./query-cache.js";
|
|
3
|
+
import { type SyncStore } from "./sync-store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Injectable Gonvex persistence over a tiny key-value backend.
|
|
6
|
+
*
|
|
7
|
+
* The client defaults to Dexie/IndexedDB. React Native has neither, so these
|
|
8
|
+
* factories implement {@link QueryCacheStore}, {@link SyncStore}, and
|
|
9
|
+
* {@link OutboxStore} against a five-method string store the host binds once —
|
|
10
|
+
* an expo-sqlite table in production, {@link createMemoryGonvexKv} in tests.
|
|
11
|
+
*/
|
|
12
|
+
export type GonvexKv = {
|
|
13
|
+
get(table: string, key: string): Promise<string | undefined>;
|
|
14
|
+
set(table: string, key: string, value: string): Promise<void>;
|
|
15
|
+
delete(table: string, key: string): Promise<void>;
|
|
16
|
+
list(table: string): Promise<Array<{
|
|
17
|
+
key: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}>>;
|
|
20
|
+
clear(table: string, keyPrefix?: string): Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
export declare const kvQueryCacheTable = "query_cache";
|
|
23
|
+
export declare const kvSyncCollectionTable = "sync_collections";
|
|
24
|
+
export declare const kvSyncDirectiveTable = "sync_directives";
|
|
25
|
+
export declare const kvMutationOutboxTable = "mutation_outbox";
|
|
26
|
+
/** An in-memory {@link GonvexKv} for tests and ephemeral sessions. */
|
|
27
|
+
export declare function createMemoryGonvexKv(): GonvexKv;
|
|
28
|
+
export type KvQueryCacheStoreOptions = {
|
|
29
|
+
maxAgeMs?: number;
|
|
30
|
+
maxEntryBytes?: number;
|
|
31
|
+
maxEntriesPerScope?: number;
|
|
32
|
+
maxBytesPerScope?: number;
|
|
33
|
+
maxBytes?: number;
|
|
34
|
+
/** Invoked whenever a write lands under a scope not seen since the last one. */
|
|
35
|
+
onScope?: (scope: string) => void | Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* A {@link QueryCacheStore} over an injected {@link GonvexKv}. Records are
|
|
39
|
+
* keyed by `scope\u0000path\u0000stableArgs`, revision-guarded like the Dexie
|
|
40
|
+
* store, and LRU-evicted by `lastAccessedAt` against per-scope and global
|
|
41
|
+
* caps. A failing backend permanently disables the cache for the session;
|
|
42
|
+
* server data remains authoritative.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createKvQueryCacheStore(kv: GonvexKv, options?: KvQueryCacheStoreOptions): QueryCacheStore;
|
|
45
|
+
export type KvSyncStoreOptions = {
|
|
46
|
+
maxBytes?: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A {@link SyncStore} over an injected {@link GonvexKv}. Each collection is
|
|
50
|
+
* one record holding its rows, cursor, and metadata; deltas are applied by
|
|
51
|
+
* key against the stored rows. Collections are LRU-evicted by
|
|
52
|
+
* `lastAccessedAt` once the table exceeds `maxBytes`. A failing backend
|
|
53
|
+
* permanently disables the store for the session.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createKvSyncStore(kv: GonvexKv, options?: KvSyncStoreOptions): SyncStore;
|
|
56
|
+
/**
|
|
57
|
+
* An {@link OutboxStore} over an injected {@link GonvexKv}. Entries are
|
|
58
|
+
* whole JSON records keyed by their queue id; ordering, causal barriers, and
|
|
59
|
+
* inflight recovery stay in {@link StoreMutationOutbox}.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createKvOutboxStore(kv: GonvexKv): OutboxStore;
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import { defaultQueryCacheMaxAgeMs, defaultQueryCacheMaxBytes, defaultQueryCacheMaxBytesPerScope, defaultQueryCacheMaxEntriesPerScope, defaultQueryCacheMaxEntryBytes, } from "./query-cache.js";
|
|
2
|
+
import { defaultSyncStoreMaxBytes, } from "./sync-store.js";
|
|
3
|
+
export const kvQueryCacheTable = "query_cache";
|
|
4
|
+
export const kvSyncCollectionTable = "sync_collections";
|
|
5
|
+
export const kvSyncDirectiveTable = "sync_directives";
|
|
6
|
+
export const kvMutationOutboxTable = "mutation_outbox";
|
|
7
|
+
/** An in-memory {@link GonvexKv} for tests and ephemeral sessions. */
|
|
8
|
+
export function createMemoryGonvexKv() {
|
|
9
|
+
const tables = new Map();
|
|
10
|
+
const table = (name) => {
|
|
11
|
+
let rows = tables.get(name);
|
|
12
|
+
if (!rows) {
|
|
13
|
+
rows = new Map();
|
|
14
|
+
tables.set(name, rows);
|
|
15
|
+
}
|
|
16
|
+
return rows;
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
async get(name, key) {
|
|
20
|
+
return table(name).get(key);
|
|
21
|
+
},
|
|
22
|
+
async set(name, key, value) {
|
|
23
|
+
table(name).set(key, value);
|
|
24
|
+
},
|
|
25
|
+
async delete(name, key) {
|
|
26
|
+
table(name).delete(key);
|
|
27
|
+
},
|
|
28
|
+
async list(name) {
|
|
29
|
+
return [...table(name).entries()].map(([key, value]) => ({ key, value }));
|
|
30
|
+
},
|
|
31
|
+
async clear(name, keyPrefix) {
|
|
32
|
+
const rows = table(name);
|
|
33
|
+
if (!keyPrefix) {
|
|
34
|
+
rows.clear();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (const key of [...rows.keys()]) {
|
|
38
|
+
if (key.startsWith(keyPrefix))
|
|
39
|
+
rows.delete(key);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A {@link QueryCacheStore} over an injected {@link GonvexKv}. Records are
|
|
46
|
+
* keyed by `scope\u0000path\u0000stableArgs`, revision-guarded like the Dexie
|
|
47
|
+
* store, and LRU-evicted by `lastAccessedAt` against per-scope and global
|
|
48
|
+
* caps. A failing backend permanently disables the cache for the session;
|
|
49
|
+
* server data remains authoritative.
|
|
50
|
+
*/
|
|
51
|
+
export function createKvQueryCacheStore(kv, options = {}) {
|
|
52
|
+
const maxAgeMs = positive(options.maxAgeMs, defaultQueryCacheMaxAgeMs);
|
|
53
|
+
const maxEntryBytes = positive(options.maxEntryBytes, defaultQueryCacheMaxEntryBytes);
|
|
54
|
+
const limits = {
|
|
55
|
+
maxEntriesPerScope: positive(options.maxEntriesPerScope, defaultQueryCacheMaxEntriesPerScope),
|
|
56
|
+
maxBytesPerScope: positive(options.maxBytesPerScope, defaultQueryCacheMaxBytesPerScope),
|
|
57
|
+
maxBytes: positive(options.maxBytes, defaultQueryCacheMaxBytes),
|
|
58
|
+
};
|
|
59
|
+
const onScope = options.onScope;
|
|
60
|
+
let lastEmittedScope;
|
|
61
|
+
let readsEnabled = true;
|
|
62
|
+
let writesEnabled = true;
|
|
63
|
+
let disabledReason;
|
|
64
|
+
const disable = (reason) => {
|
|
65
|
+
readsEnabled = false;
|
|
66
|
+
writesEnabled = false;
|
|
67
|
+
disabledReason = reason;
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
async read(scope, path, args, ageMs = maxAgeMs) {
|
|
71
|
+
if (!readsEnabled || !scope)
|
|
72
|
+
return undefined;
|
|
73
|
+
try {
|
|
74
|
+
const key = storageKey(scope, path, args);
|
|
75
|
+
const record = parseJson(await kv.get(kvQueryCacheTable, key));
|
|
76
|
+
if (!record)
|
|
77
|
+
return undefined;
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
if (now - record.writtenAt > Math.min(ageMs, maxAgeMs)) {
|
|
80
|
+
await kv.delete(kvQueryCacheTable, key);
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
record.lastAccessedAt = now;
|
|
84
|
+
await kv.set(kvQueryCacheTable, key, JSON.stringify(record));
|
|
85
|
+
return {
|
|
86
|
+
result: record.result,
|
|
87
|
+
revision: record.revision,
|
|
88
|
+
writtenAt: record.writtenAt,
|
|
89
|
+
ageMs: now - record.writtenAt,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
disable(errorReason(error, "kv-query-cache-read-failed"));
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async write(value) {
|
|
98
|
+
if (!writesEnabled || !value.scope || !value.revision)
|
|
99
|
+
return "disabled";
|
|
100
|
+
try {
|
|
101
|
+
const sizeBytes = jsonSize(value.result);
|
|
102
|
+
if (sizeBytes > maxEntryBytes)
|
|
103
|
+
return "oversize";
|
|
104
|
+
const key = storageKey(value.scope, value.path, value.args);
|
|
105
|
+
const existing = parseJson(await kv.get(kvQueryCacheTable, key));
|
|
106
|
+
if (existing && existing.revision >= value.revision)
|
|
107
|
+
return "older";
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
const record = {
|
|
110
|
+
scope: value.scope,
|
|
111
|
+
path: value.path,
|
|
112
|
+
result: value.result,
|
|
113
|
+
revision: value.revision,
|
|
114
|
+
writtenAt: now,
|
|
115
|
+
lastAccessedAt: now,
|
|
116
|
+
sizeBytes,
|
|
117
|
+
};
|
|
118
|
+
await kv.set(kvQueryCacheTable, key, JSON.stringify(record));
|
|
119
|
+
await evictQueryCache(kv, limits);
|
|
120
|
+
if (onScope && lastEmittedScope !== value.scope) {
|
|
121
|
+
lastEmittedScope = value.scope;
|
|
122
|
+
await onScope(value.scope);
|
|
123
|
+
}
|
|
124
|
+
return "written";
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
disable(errorReason(error, "kv-query-cache-write-failed"));
|
|
128
|
+
return "error";
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
async delete(scope, path, args) {
|
|
132
|
+
try {
|
|
133
|
+
await kv.delete(kvQueryCacheTable, storageKey(scope, path, args));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Cache deletion is best-effort; server data remains authoritative.
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
async clear(scope) {
|
|
140
|
+
try {
|
|
141
|
+
await kv.clear(kvQueryCacheTable, scope ? `${scope}\u0000` : undefined);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Clearing a disposable cache must not affect the application.
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
status() {
|
|
148
|
+
return {
|
|
149
|
+
enabled: readsEnabled || writesEnabled,
|
|
150
|
+
readsEnabled,
|
|
151
|
+
writesEnabled,
|
|
152
|
+
reason: disabledReason,
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
close() {
|
|
156
|
+
// The KV connection belongs to the host and outlives this store.
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async function evictQueryCache(kv, limits) {
|
|
161
|
+
const rows = [];
|
|
162
|
+
const corrupt = [];
|
|
163
|
+
for (const { key, value } of await kv.list(kvQueryCacheTable)) {
|
|
164
|
+
const record = parseJson(value);
|
|
165
|
+
if (record)
|
|
166
|
+
rows.push({ key, record });
|
|
167
|
+
else
|
|
168
|
+
corrupt.push(key);
|
|
169
|
+
}
|
|
170
|
+
rows.sort((left, right) => left.record.lastAccessedAt - right.record.lastAccessedAt);
|
|
171
|
+
const byScope = new Map();
|
|
172
|
+
for (const row of rows) {
|
|
173
|
+
const list = byScope.get(row.record.scope) ?? [];
|
|
174
|
+
list.push(row);
|
|
175
|
+
byScope.set(row.record.scope, list);
|
|
176
|
+
}
|
|
177
|
+
const evict = new Set(corrupt);
|
|
178
|
+
let totalBytes = 0;
|
|
179
|
+
for (const list of byScope.values()) {
|
|
180
|
+
let scopeBytes = 0;
|
|
181
|
+
let scopeCount = 0;
|
|
182
|
+
// Newest first: eviction always drops the least recently used entries.
|
|
183
|
+
for (let index = list.length - 1; index >= 0; index -= 1) {
|
|
184
|
+
const row = list[index];
|
|
185
|
+
if (scopeCount >= limits.maxEntriesPerScope
|
|
186
|
+
|| scopeBytes + row.record.sizeBytes > limits.maxBytesPerScope) {
|
|
187
|
+
evict.add(row.key);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
scopeCount += 1;
|
|
191
|
+
scopeBytes += row.record.sizeBytes;
|
|
192
|
+
totalBytes += row.record.sizeBytes;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
for (const row of rows) {
|
|
196
|
+
if (totalBytes <= limits.maxBytes)
|
|
197
|
+
break;
|
|
198
|
+
if (evict.has(row.key))
|
|
199
|
+
continue;
|
|
200
|
+
evict.add(row.key);
|
|
201
|
+
totalBytes -= row.record.sizeBytes;
|
|
202
|
+
}
|
|
203
|
+
for (const key of evict)
|
|
204
|
+
await kv.delete(kvQueryCacheTable, key);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* A {@link SyncStore} over an injected {@link GonvexKv}. Each collection is
|
|
208
|
+
* one record holding its rows, cursor, and metadata; deltas are applied by
|
|
209
|
+
* key against the stored rows. Collections are LRU-evicted by
|
|
210
|
+
* `lastAccessedAt` once the table exceeds `maxBytes`. A failing backend
|
|
211
|
+
* permanently disables the store for the session.
|
|
212
|
+
*/
|
|
213
|
+
export function createKvSyncStore(kv, options = {}) {
|
|
214
|
+
const maxBytes = positive(options.maxBytes, defaultSyncStoreMaxBytes);
|
|
215
|
+
let disabled = false;
|
|
216
|
+
const loadRecord = async (key) => parseJson(await kv.get(kvSyncCollectionTable, key));
|
|
217
|
+
const persist = async (key, value) => {
|
|
218
|
+
const record = {
|
|
219
|
+
...value,
|
|
220
|
+
rowsUnchanged: undefined,
|
|
221
|
+
lastAccessedAt: Date.now(),
|
|
222
|
+
sizeBytes: jsonSize(value.rows),
|
|
223
|
+
};
|
|
224
|
+
await kv.set(kvSyncCollectionTable, key, JSON.stringify(record));
|
|
225
|
+
await evictSyncCollections(kv, maxBytes);
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
async load(scope, path, args) {
|
|
229
|
+
if (disabled || !scope)
|
|
230
|
+
return undefined;
|
|
231
|
+
try {
|
|
232
|
+
const key = storageKey(scope, path, args);
|
|
233
|
+
const record = await loadRecord(key);
|
|
234
|
+
if (!record)
|
|
235
|
+
return undefined;
|
|
236
|
+
record.lastAccessedAt = Date.now();
|
|
237
|
+
await kv.set(kvSyncCollectionTable, key, JSON.stringify(record));
|
|
238
|
+
const { lastAccessedAt: _lastAccessedAt, sizeBytes: _sizeBytes, ...collection } = record;
|
|
239
|
+
return collection;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
disabled = true;
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
async replace(scope, path, args, value) {
|
|
247
|
+
if (disabled || !scope)
|
|
248
|
+
return;
|
|
249
|
+
try {
|
|
250
|
+
const key = storageKey(scope, path, args);
|
|
251
|
+
if (value.rowsUnchanged) {
|
|
252
|
+
const existing = await loadRecord(key);
|
|
253
|
+
// The caller proved the rows are byte-identical, so only advance
|
|
254
|
+
// the cursor/metadata. A missing record falls through to a full
|
|
255
|
+
// write.
|
|
256
|
+
if (existing) {
|
|
257
|
+
await persist(key, {
|
|
258
|
+
...existing,
|
|
259
|
+
cursor: value.cursor,
|
|
260
|
+
keyField: value.keyField,
|
|
261
|
+
mode: value.mode,
|
|
262
|
+
truncated: value.truncated,
|
|
263
|
+
orderBy: value.orderBy,
|
|
264
|
+
orderDirection: value.orderDirection,
|
|
265
|
+
maxRows: value.maxRows,
|
|
266
|
+
maxBytes: value.maxBytes,
|
|
267
|
+
hashes: value.hashes,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
await persist(key, value);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
disabled = true;
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
async applyDelta(scope, path, args, value) {
|
|
279
|
+
if (disabled || !scope)
|
|
280
|
+
return;
|
|
281
|
+
try {
|
|
282
|
+
const key = storageKey(scope, path, args);
|
|
283
|
+
const existing = await loadRecord(key);
|
|
284
|
+
const keyField = value.keyField || existing?.keyField || "_id";
|
|
285
|
+
const deleted = new Set(value.deleted.map(String));
|
|
286
|
+
const kept = (existing?.rows ?? []).filter((row) => !deleted.has(rowKey(row, keyField)));
|
|
287
|
+
const byKey = new Map(kept.map((row) => [rowKey(row, keyField), row]));
|
|
288
|
+
for (const upsert of value.upserts) {
|
|
289
|
+
const id = rowKey(upsert, keyField);
|
|
290
|
+
if (!id)
|
|
291
|
+
continue;
|
|
292
|
+
byKey.set(id, upsert);
|
|
293
|
+
}
|
|
294
|
+
await persist(key, {
|
|
295
|
+
rows: [...byKey.values()],
|
|
296
|
+
cursor: value.cursor,
|
|
297
|
+
keyField,
|
|
298
|
+
mode: value.mode ?? existing?.mode,
|
|
299
|
+
truncated: value.truncated,
|
|
300
|
+
orderBy: value.orderBy ?? existing?.orderBy,
|
|
301
|
+
orderDirection: value.orderDirection ?? existing?.orderDirection,
|
|
302
|
+
maxRows: value.maxRows ?? existing?.maxRows,
|
|
303
|
+
maxBytes: value.maxBytes ?? existing?.maxBytes,
|
|
304
|
+
hashes: value.hashes ?? existing?.hashes,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
disabled = true;
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
async delete(scope, path, args) {
|
|
312
|
+
try {
|
|
313
|
+
await kv.delete(kvSyncCollectionTable, storageKey(scope, path, args));
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// Disposable local data must never break the application.
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
async loadDirective(identity) {
|
|
320
|
+
if (!identity)
|
|
321
|
+
return undefined;
|
|
322
|
+
try {
|
|
323
|
+
return parseJson(await kv.get(kvSyncDirectiveTable, identity));
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
async saveDirective(identity, directive) {
|
|
330
|
+
if (!identity)
|
|
331
|
+
return;
|
|
332
|
+
try {
|
|
333
|
+
await kv.set(kvSyncDirectiveTable, identity, JSON.stringify(directive));
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// Warm-start metadata is an optimization, never a request prerequisite.
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
async clear(scope) {
|
|
340
|
+
try {
|
|
341
|
+
await kv.clear(kvSyncCollectionTable, scope ? `${scope}\u0000` : undefined);
|
|
342
|
+
if (!scope)
|
|
343
|
+
await kv.clear(kvSyncDirectiveTable);
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// Disposable local data must never break the application.
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
close() {
|
|
350
|
+
// The KV connection belongs to the host and outlives this store.
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
async function evictSyncCollections(kv, maxBytes) {
|
|
355
|
+
const rows = [];
|
|
356
|
+
for (const { key, value } of await kv.list(kvSyncCollectionTable)) {
|
|
357
|
+
const record = parseJson(value);
|
|
358
|
+
if (record)
|
|
359
|
+
rows.push({ key, record });
|
|
360
|
+
else
|
|
361
|
+
await kv.delete(kvSyncCollectionTable, key);
|
|
362
|
+
}
|
|
363
|
+
rows.sort((left, right) => left.record.lastAccessedAt - right.record.lastAccessedAt);
|
|
364
|
+
let total = rows.reduce((sum, row) => sum + (row.record.sizeBytes || 0), 0);
|
|
365
|
+
for (const row of rows) {
|
|
366
|
+
if (total <= maxBytes)
|
|
367
|
+
break;
|
|
368
|
+
await kv.delete(kvSyncCollectionTable, row.key);
|
|
369
|
+
total -= row.record.sizeBytes || 0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* An {@link OutboxStore} over an injected {@link GonvexKv}. Entries are
|
|
374
|
+
* whole JSON records keyed by their queue id; ordering, causal barriers, and
|
|
375
|
+
* inflight recovery stay in {@link StoreMutationOutbox}.
|
|
376
|
+
*/
|
|
377
|
+
export function createKvOutboxStore(kv) {
|
|
378
|
+
return {
|
|
379
|
+
async load() {
|
|
380
|
+
const entries = [];
|
|
381
|
+
for (const { value } of await kv.list(kvMutationOutboxTable)) {
|
|
382
|
+
// A corrupt row must not strand every other queued mutation.
|
|
383
|
+
const entry = parseJson(value);
|
|
384
|
+
if (entry && typeof entry.id === "number")
|
|
385
|
+
entries.push(entry);
|
|
386
|
+
}
|
|
387
|
+
return entries.sort((left, right) => left.id - right.id);
|
|
388
|
+
},
|
|
389
|
+
async put(entry) {
|
|
390
|
+
await kv.set(kvMutationOutboxTable, String(entry.id), JSON.stringify(entry));
|
|
391
|
+
},
|
|
392
|
+
async delete(id) {
|
|
393
|
+
await kv.delete(kvMutationOutboxTable, String(id));
|
|
394
|
+
},
|
|
395
|
+
async clear(scope) {
|
|
396
|
+
if (scope === undefined) {
|
|
397
|
+
await kv.clear(kvMutationOutboxTable);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
for (const { key, value } of await kv.list(kvMutationOutboxTable)) {
|
|
401
|
+
if (parseJson(value)?.scope === scope) {
|
|
402
|
+
await kv.delete(kvMutationOutboxTable, key);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function storageKey(scope, path, args) {
|
|
409
|
+
return `${scope}\u0000${path}\u0000${stableStringify(args ?? {})}`;
|
|
410
|
+
}
|
|
411
|
+
function stableStringify(value) {
|
|
412
|
+
if (value === null || typeof value !== "object")
|
|
413
|
+
return JSON.stringify(value);
|
|
414
|
+
if (Array.isArray(value))
|
|
415
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
416
|
+
const record = value;
|
|
417
|
+
return `{${Object.keys(record)
|
|
418
|
+
.sort()
|
|
419
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
|
|
420
|
+
.join(",")}}`;
|
|
421
|
+
}
|
|
422
|
+
function parseJson(value) {
|
|
423
|
+
if (value === undefined)
|
|
424
|
+
return undefined;
|
|
425
|
+
try {
|
|
426
|
+
return JSON.parse(value);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function rowKey(row, keyField) {
|
|
433
|
+
if (!row || Array.isArray(row) || typeof row !== "object")
|
|
434
|
+
return "";
|
|
435
|
+
const candidate = row[keyField];
|
|
436
|
+
return typeof candidate === "string" || typeof candidate === "number" ? String(candidate) : "";
|
|
437
|
+
}
|
|
438
|
+
function jsonSize(value) {
|
|
439
|
+
return new TextEncoder().encode(JSON.stringify(value ?? null)).byteLength;
|
|
440
|
+
}
|
|
441
|
+
function errorReason(error, fallback) {
|
|
442
|
+
return error instanceof Error ? error.message || error.name : fallback;
|
|
443
|
+
}
|
|
444
|
+
function positive(value, fallback) {
|
|
445
|
+
return value !== undefined && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
446
|
+
}
|
|
447
|
+
//# sourceMappingURL=kv-stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-stores.js","sourceRoot":"","sources":["../src/kv-stores.ts"],"names":[],"mappings":"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"}
|
package/dist/outbox.d.ts
CHANGED
|
@@ -4,6 +4,26 @@ export type MutationOutboxOptions = {
|
|
|
4
4
|
indexedDB?: IDBFactory;
|
|
5
5
|
IDBKeyRange?: typeof IDBKeyRange;
|
|
6
6
|
enabled?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Injectable durable record storage for runtimes without IndexedDB (React
|
|
9
|
+
* Native). When provided, the queue semantics still run in the SDK; the
|
|
10
|
+
* store only persists entries so they survive an app restart.
|
|
11
|
+
*/
|
|
12
|
+
store?: OutboxStore;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A dumb durable record store backing {@link StoreMutationOutbox}. It holds
|
|
16
|
+
* whole entries keyed by id and knows nothing about queue semantics — total
|
|
17
|
+
* order, causal barriers, and inflight recovery all stay in the SDK.
|
|
18
|
+
*/
|
|
19
|
+
export type OutboxStore = {
|
|
20
|
+
/** Every persisted entry across all scopes; called once to hydrate. */
|
|
21
|
+
load(): Promise<MutationOutboxEntry[]>;
|
|
22
|
+
/** Insert or replace the entry with this id. */
|
|
23
|
+
put(entry: MutationOutboxEntry): Promise<void>;
|
|
24
|
+
delete(id: number): Promise<void>;
|
|
25
|
+
clear(scope?: string): Promise<void>;
|
|
26
|
+
close?(): void;
|
|
7
27
|
};
|
|
8
28
|
export type MutationOutboxEntry = {
|
|
9
29
|
/** Auto-incremented sequence number. Lower ids always happened first. */
|
|
@@ -93,4 +113,42 @@ export declare class DexieMutationOutbox implements MutationOutbox {
|
|
|
93
113
|
private open;
|
|
94
114
|
private createDatabase;
|
|
95
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* The same totally ordered queue semantics as {@link DexieMutationOutbox},
|
|
118
|
+
* run entirely in memory with every state transition written through an
|
|
119
|
+
* injected {@link OutboxStore}. Hydration replays `store.load()` once and
|
|
120
|
+
* seeds the auto-increment id from the highest persisted id, so replayed
|
|
121
|
+
* entries keep their original ids, idempotency keys, and enqueue order.
|
|
122
|
+
*
|
|
123
|
+
* The store is an optimization for durability, not a prerequisite for the
|
|
124
|
+
* optimistic mutation path. A failing store permanently degrades this
|
|
125
|
+
* instance to the same queue semantics in memory for the current session.
|
|
126
|
+
*/
|
|
127
|
+
export declare class StoreMutationOutbox implements MutationOutbox {
|
|
128
|
+
private readonly store;
|
|
129
|
+
private readonly listeners;
|
|
130
|
+
private readonly entries;
|
|
131
|
+
private readonly ready;
|
|
132
|
+
private memoryOnly;
|
|
133
|
+
private nextId;
|
|
134
|
+
constructor(store: OutboxStore, options?: {
|
|
135
|
+
enabled?: boolean;
|
|
136
|
+
});
|
|
137
|
+
enqueue(mutation: EnqueueMutation): Promise<MutationOutboxEntry>;
|
|
138
|
+
loadAll(scope: string): Promise<MutationOutboxEntry[]>;
|
|
139
|
+
nextReady(scope: string, now: number): Promise<MutationOutboxEntry | undefined>;
|
|
140
|
+
markInflight(id: number): Promise<void>;
|
|
141
|
+
markCommitted(id: number): Promise<void>;
|
|
142
|
+
ack(id: number): Promise<void>;
|
|
143
|
+
fail(id: number, error: string): Promise<void>;
|
|
144
|
+
count(scope: string): Promise<number>;
|
|
145
|
+
clear(scope: string): Promise<void>;
|
|
146
|
+
subscribe(listener: () => void): () => void;
|
|
147
|
+
private hydrate;
|
|
148
|
+
private persistPut;
|
|
149
|
+
private persistDelete;
|
|
150
|
+
private sortedEntries;
|
|
151
|
+
private notify;
|
|
152
|
+
private degradeToMemory;
|
|
153
|
+
}
|
|
96
154
|
export declare function createMutationOutbox(options?: MutationOutboxOptions): MutationOutbox;
|