@procedures/store 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.md +22 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/storage/api.d.ts +11 -0
- package/dist/storage/api.d.ts.map +1 -1
- package/dist/storage/api.js +36 -3
- package/dist/storage/api.js.map +1 -1
- package/dist/storage/hybrid.d.ts +49 -7
- package/dist/storage/hybrid.d.ts.map +1 -1
- package/dist/storage/hybrid.js +124 -36
- package/dist/storage/hybrid.js.map +1 -1
- package/dist/storage/synced-registry.d.ts +8 -1
- package/dist/storage/synced-registry.d.ts.map +1 -1
- package/dist/storage/synced-registry.js +25 -4
- package/dist/storage/synced-registry.js.map +1 -1
- package/package.json +3 -3
- package/src/storage/api.ts +35 -3
- package/src/storage/hybrid.ts +117 -33
- package/src/storage/synced-registry.ts +26 -6
package/dist/storage/api.d.ts
CHANGED
|
@@ -104,7 +104,13 @@ export interface ApiStorageOptions {
|
|
|
104
104
|
export declare class ApiStorage<T> implements CollectionStorage<T> {
|
|
105
105
|
private client;
|
|
106
106
|
private options;
|
|
107
|
+
private closed;
|
|
107
108
|
constructor(client: Client, options: ApiStorageOptions);
|
|
109
|
+
/**
|
|
110
|
+
* The item of an id. For the core collection procedures, the call asks for `{ found, value }`,
|
|
111
|
+
* so a stored `null` reads back as `null`. A custom `service` or `get` operation gets `{ id }`,
|
|
112
|
+
* and its `null` reads back as `undefined`.
|
|
113
|
+
*/
|
|
108
114
|
get(id: string): Promise<T | undefined>;
|
|
109
115
|
getAll(): Promise<T[]>;
|
|
110
116
|
/** Every item with its id, from the `entries` procedure of the collection (roadmap R2). */
|
|
@@ -124,6 +130,11 @@ export declare class ApiStorage<T> implements CollectionStorage<T> {
|
|
|
124
130
|
setBatch(items: Array<[string, T]>): Promise<void>;
|
|
125
131
|
deleteBatch(ids: string[]): Promise<number>;
|
|
126
132
|
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
133
|
+
/**
|
|
134
|
+
* This method closes the storage. After it, each operation rejects, and a second `close()` does
|
|
135
|
+
* nothing. The items stay on the server. The client belongs to the caller, unless the option
|
|
136
|
+
* `closeClient` gave it to the storage.
|
|
137
|
+
*/
|
|
127
138
|
close(): Promise<void>;
|
|
128
139
|
getMetadata(): Promise<StorageMetadata>;
|
|
129
140
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/storage/api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAClG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGjD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE;QACX,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF;;;OAGG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;IAEnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAEzB;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,CAAC;IAEtB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IAErB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/storage/api.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/api.js
CHANGED
|
@@ -31,6 +31,16 @@ function untilAborted(promise, signal, describe) {
|
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* True for the output `{ found, value }` of the core procedure `get` with `found: true`: an
|
|
36
|
+
* object with a boolean `found`, and no key other than `found` and `value`.
|
|
37
|
+
*/
|
|
38
|
+
function isFoundOutput(value) {
|
|
39
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
40
|
+
return false;
|
|
41
|
+
const record = value;
|
|
42
|
+
return typeof record["found"] === "boolean" && Object.keys(record).every((key) => key === "found" || key === "value");
|
|
43
|
+
}
|
|
34
44
|
/**
|
|
35
45
|
* API storage backed by universal client.
|
|
36
46
|
*
|
|
@@ -61,6 +71,7 @@ function untilAborted(promise, signal, describe) {
|
|
|
61
71
|
export class ApiStorage {
|
|
62
72
|
client;
|
|
63
73
|
options;
|
|
74
|
+
closed = false;
|
|
64
75
|
constructor(client, options) {
|
|
65
76
|
const service = options.service ?? (options.collection !== undefined ? `collections.${options.collection}` : undefined);
|
|
66
77
|
if (!service) {
|
|
@@ -91,14 +102,27 @@ export class ApiStorage {
|
|
|
91
102
|
retryDelay: optionalMs(options.retryDelay, "retryDelay") ?? 100,
|
|
92
103
|
...(options.signal !== undefined && { signal: options.signal }),
|
|
93
104
|
closeClient: options.closeClient ?? false,
|
|
105
|
+
// A custom service can refuse an unknown input field: it gets the input { id }
|
|
106
|
+
askFound: options.service === undefined && (options.operations?.get ?? "get") === "get",
|
|
94
107
|
};
|
|
95
108
|
}
|
|
96
109
|
//
|
|
97
110
|
// ═══ Read Operations ═══
|
|
98
111
|
//
|
|
112
|
+
/**
|
|
113
|
+
* The item of an id. For the core collection procedures, the call asks for `{ found, value }`,
|
|
114
|
+
* so a stored `null` reads back as `null`. A custom `service` or `get` operation gets `{ id }`,
|
|
115
|
+
* and its `null` reads back as `undefined`.
|
|
116
|
+
*/
|
|
99
117
|
async get(id) {
|
|
100
|
-
|
|
101
|
-
|
|
118
|
+
if (!this.options.askFound) {
|
|
119
|
+
return (await this.call("get", { id })) ?? undefined;
|
|
120
|
+
}
|
|
121
|
+
const result = await this.call("get", { id, found: true });
|
|
122
|
+
if (isFoundOutput(result))
|
|
123
|
+
return result.found ? result.value : undefined;
|
|
124
|
+
// A server before `found` ignores it and gives the item, or null for a missing id
|
|
125
|
+
return (result ?? undefined);
|
|
102
126
|
}
|
|
103
127
|
async getAll() {
|
|
104
128
|
const values = await this.call("getAll", {});
|
|
@@ -178,8 +202,15 @@ export class ApiStorage {
|
|
|
178
202
|
//
|
|
179
203
|
// ═══ Lifecycle & Metadata ═══
|
|
180
204
|
//
|
|
205
|
+
/**
|
|
206
|
+
* This method closes the storage. After it, each operation rejects, and a second `close()` does
|
|
207
|
+
* nothing. The items stay on the server. The client belongs to the caller, unless the option
|
|
208
|
+
* `closeClient` gave it to the storage.
|
|
209
|
+
*/
|
|
181
210
|
async close() {
|
|
182
|
-
|
|
211
|
+
if (this.closed)
|
|
212
|
+
return;
|
|
213
|
+
this.closed = true;
|
|
183
214
|
if (this.options.closeClient) {
|
|
184
215
|
await this.client.close();
|
|
185
216
|
}
|
|
@@ -203,6 +234,8 @@ export class ApiStorage {
|
|
|
203
234
|
* Make an RPC call via the universal client, with the timeout, the signal and the retries.
|
|
204
235
|
*/
|
|
205
236
|
async call(operation, payload) {
|
|
237
|
+
if (this.closed)
|
|
238
|
+
throw new Error(`The ApiStorage of ${this.options.service} is closed`);
|
|
206
239
|
const method = {
|
|
207
240
|
service: this.options.service,
|
|
208
241
|
operation: this.options.operations[operation] ?? operation,
|
package/dist/storage/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/storage/api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAE,UAAU,EAAiB,MAAM,oBAAoB,CAAC;AA4E/D,mFAAmF;AACnF,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAiC,CAAC,SAAS,KAAK,IAAI,CACtD,CAAC;AACJ,CAAC;AAED,2DAA2D;AAC3D,SAAS,YAAY,CAAI,OAAmB,EAAE,MAA+B,EAAE,QAAqB;IAClG,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IACtD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CACV,CAAC,KAAK,EAAE,EAAE;YACR,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;YACjB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,UAAU;IACb,MAAM,CAAS;IACf,OAAO,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/storage/api.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/hybrid.d.ts
CHANGED
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
* - A sync asks for the changes since a version when the remote has `getChangesSince`.
|
|
17
17
|
* - With `versionOf`, a conflict is a change on both sides, and `conflictResolution` decides.
|
|
18
18
|
* - `pullInterval` keeps the local copy fresh.
|
|
19
|
+
* - Each write takes a copy of the value (`structuredClone`), for the local copy, the queue and the
|
|
20
|
+
* remote. The reads give the copies of the local copy, or of the remote in `remote-only` mode.
|
|
21
|
+
* - A full sync reads the local ids with `getIds()`, so it does not copy each local item.
|
|
22
|
+
* - After `close()`, each operation rejects, and a second `close()` does nothing.
|
|
19
23
|
*/
|
|
20
24
|
import type { CollectionStorage, StorageMetadata } from "@procedures/collections";
|
|
21
25
|
import { type Duration } from "@procedures/client";
|
|
@@ -198,7 +202,8 @@ export declare class ModeSwitchError extends Error {
|
|
|
198
202
|
/**
|
|
199
203
|
* A storage for the local copy of a `HybridStorage` (roadmap R45): a collection storage that gives
|
|
200
204
|
* its entries. A durable one, for example `SqliteStorage` of `@procedures/sqlite`, keeps the local
|
|
201
|
-
* copy after a restart.
|
|
205
|
+
* copy after a restart. With `getIds()`, a full sync reads the local ids without a copy of the
|
|
206
|
+
* values.
|
|
202
207
|
*/
|
|
203
208
|
export type LocalCopyStorage<T> = CollectionStorage<T> & {
|
|
204
209
|
getEntries(): Promise<Array<[string, T]>>;
|
|
@@ -264,7 +269,10 @@ export declare class HybridStorage<T> implements CollectionStorage<T> {
|
|
|
264
269
|
private active;
|
|
265
270
|
private idleWaiters;
|
|
266
271
|
private isOnline;
|
|
272
|
+
/** True after `close()`: each operation rejects */
|
|
267
273
|
private closed;
|
|
274
|
+
/** The work of the first `close()` */
|
|
275
|
+
private closing;
|
|
268
276
|
/** A counter of local writes for each id, so a sync does not restore an older remote value */
|
|
269
277
|
private readonly writeVersions;
|
|
270
278
|
private clearVersion;
|
|
@@ -310,16 +318,27 @@ export declare class HybridStorage<T> implements CollectionStorage<T> {
|
|
|
310
318
|
getAll(): Promise<T[]>;
|
|
311
319
|
/** Every item with its id. */
|
|
312
320
|
getEntries(): Promise<Array<[string, T]>>;
|
|
321
|
+
/**
|
|
322
|
+
* The ids of the items, in the order of the entries: the ids of the local copy, or of the remote
|
|
323
|
+
* in `remote-only` mode. The ids come without a copy of the values when the storage under them
|
|
324
|
+
* has `getIds()`.
|
|
325
|
+
*/
|
|
326
|
+
getIds(): Promise<string[]>;
|
|
313
327
|
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
314
328
|
has(id: string): Promise<boolean>;
|
|
315
329
|
size(): Promise<number>;
|
|
316
330
|
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
317
331
|
/** A read of the remote-only mode. A queue that a restart loaded reaches the remote first. */
|
|
318
332
|
private remoteRead;
|
|
319
|
-
|
|
333
|
+
/**
|
|
334
|
+
* This method sets an item. The storage takes a copy of the value, so a later change of the
|
|
335
|
+
* value does not change the local copy, the queue or the remote write.
|
|
336
|
+
*/
|
|
337
|
+
set(id: string, written: T): Promise<void>;
|
|
320
338
|
delete(id: string): Promise<boolean>;
|
|
321
339
|
clear(): Promise<void>;
|
|
322
|
-
|
|
340
|
+
/** This method sets several items. The storage takes a copy of each value, as `set` does. */
|
|
341
|
+
setBatch(written: Array<[string, T]>): Promise<void>;
|
|
323
342
|
deleteBatch(ids: string[]): Promise<number>;
|
|
324
343
|
/**
|
|
325
344
|
* One write, in the mode of the storage. The result is the result of the local copy, or of the
|
|
@@ -375,12 +394,31 @@ export declare class HybridStorage<T> implements CollectionStorage<T> {
|
|
|
375
394
|
* Force a full sync (both directions).
|
|
376
395
|
*/
|
|
377
396
|
sync(): Promise<void>;
|
|
378
|
-
/**
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
397
|
+
/** The pull of `syncFromRemote()`, after the syncs that run. */
|
|
398
|
+
private pull;
|
|
399
|
+
/** The push of `syncToRemote()`, after the syncs that run. */
|
|
400
|
+
private push;
|
|
401
|
+
/** The error of an operation after `close()`. */
|
|
402
|
+
private closedError;
|
|
403
|
+
/**
|
|
404
|
+
* This method closes the storage.
|
|
405
|
+
*
|
|
406
|
+
* - It stops the timers, and waits for the operations, the mode switch and the syncs that run.
|
|
407
|
+
* - It sends the queue (not in `local-only` mode). The queue store keeps the operations that
|
|
408
|
+
* the remote did not take.
|
|
409
|
+
* - It closes the remote and the local copy. A durable local copy keeps its items.
|
|
410
|
+
* - After it, each operation rejects. A second `close()` does nothing: it gives the promise of
|
|
411
|
+
* the first one, without its error.
|
|
382
412
|
*/
|
|
383
413
|
close(): Promise<void>;
|
|
414
|
+
/** The work of `close()`. */
|
|
415
|
+
private shutDown;
|
|
416
|
+
/**
|
|
417
|
+
* The metadata of the storage. `size` is the result of `size()`: the number of items of the
|
|
418
|
+
* storage, also in `remote-only` mode. `stats.localSize` is the number of items of the local
|
|
419
|
+
* copy. Before, `size` was the size of the local copy, which `remote-only` keeps empty (found by
|
|
420
|
+
* the contract suite of `@procedures/collections/testing`).
|
|
421
|
+
*/
|
|
384
422
|
getMetadata(): Promise<StorageMetadata>;
|
|
385
423
|
/**
|
|
386
424
|
* This method starts a public operation: after the load of the queue (a write) or the first sync (a read),
|
|
@@ -429,6 +467,10 @@ export declare class HybridStorage<T> implements CollectionStorage<T> {
|
|
|
429
467
|
* so the remote list without ids emptied the cache (roadmap R2).
|
|
430
468
|
*/
|
|
431
469
|
private remoteEntries;
|
|
470
|
+
/** The ids of the local copy: from `getIds()` when it has it, without a copy of the values. */
|
|
471
|
+
private localIds;
|
|
472
|
+
/** The ids of the remote: from `getIds()` when it has it, or else from its entries. */
|
|
473
|
+
private remoteIds;
|
|
432
474
|
/**
|
|
433
475
|
* This method sends the queue. The operations that reached the remote leave the queue. The queue store
|
|
434
476
|
* gets the change at the end.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/storage/hybrid.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/storage/hybrid.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/hybrid.js
CHANGED
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
* - A sync asks for the changes since a version when the remote has `getChangesSince`.
|
|
17
17
|
* - With `versionOf`, a conflict is a change on both sides, and `conflictResolution` decides.
|
|
18
18
|
* - `pullInterval` keeps the local copy fresh.
|
|
19
|
+
* - Each write takes a copy of the value (`structuredClone`), for the local copy, the queue and the
|
|
20
|
+
* remote. The reads give the copies of the local copy, or of the remote in `remote-only` mode.
|
|
21
|
+
* - A full sync reads the local ids with `getIds()`, so it does not copy each local item.
|
|
22
|
+
* - After `close()`, each operation rejects, and a second `close()` does nothing.
|
|
19
23
|
*/
|
|
20
24
|
import { InMemoryStorage } from "@procedures/collections";
|
|
21
25
|
import { optionalMs } from "@procedures/client";
|
|
@@ -167,7 +171,10 @@ export class HybridStorage {
|
|
|
167
171
|
active = 0;
|
|
168
172
|
idleWaiters = [];
|
|
169
173
|
isOnline = true;
|
|
174
|
+
/** True after `close()`: each operation rejects */
|
|
170
175
|
closed = false;
|
|
176
|
+
/** The work of the first `close()` */
|
|
177
|
+
closing;
|
|
171
178
|
/** A counter of local writes for each id, so a sync does not restore an older remote value */
|
|
172
179
|
writeVersions = new Map();
|
|
173
180
|
clearVersion = 0;
|
|
@@ -229,6 +236,8 @@ export class HybridStorage {
|
|
|
229
236
|
*/
|
|
230
237
|
async setMode(mode) {
|
|
231
238
|
const target = checkMode(mode);
|
|
239
|
+
if (this.closed)
|
|
240
|
+
throw this.closedError();
|
|
232
241
|
const previous = this.gate;
|
|
233
242
|
let release;
|
|
234
243
|
this.gate = new Promise((resolve) => (release = resolve));
|
|
@@ -288,7 +297,9 @@ export class HybridStorage {
|
|
|
288
297
|
const push = this.mode === "write-back" && this.options.syncInterval > 0;
|
|
289
298
|
if (push && !this.syncTimer) {
|
|
290
299
|
this.syncTimer = setInterval(() => {
|
|
291
|
-
this.
|
|
300
|
+
if (this.closed)
|
|
301
|
+
return;
|
|
302
|
+
this.push().catch((err) => {
|
|
292
303
|
console.error("Periodic sync failed:", err);
|
|
293
304
|
});
|
|
294
305
|
}, this.options.syncInterval);
|
|
@@ -302,7 +313,9 @@ export class HybridStorage {
|
|
|
302
313
|
const pull = this.options.pullInterval > 0 && (this.mode === "write-through" || this.mode === "write-back");
|
|
303
314
|
if (pull && !this.pullTimer) {
|
|
304
315
|
this.pullTimer = setInterval(() => {
|
|
305
|
-
this.
|
|
316
|
+
if (this.closed)
|
|
317
|
+
return;
|
|
318
|
+
this.pull().catch((err) => {
|
|
306
319
|
console.error("Periodic pull failed:", err);
|
|
307
320
|
});
|
|
308
321
|
}, this.options.pullInterval);
|
|
@@ -364,6 +377,14 @@ export class HybridStorage {
|
|
|
364
377
|
async getEntries() {
|
|
365
378
|
return this.run(true, () => (this.mode === "remote-only" ? this.remoteRead(() => this.remoteEntries()) : this.local.getEntries()));
|
|
366
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* The ids of the items, in the order of the entries: the ids of the local copy, or of the remote
|
|
382
|
+
* in `remote-only` mode. The ids come without a copy of the values when the storage under them
|
|
383
|
+
* has `getIds()`.
|
|
384
|
+
*/
|
|
385
|
+
async getIds() {
|
|
386
|
+
return this.run(true, () => (this.mode === "remote-only" ? this.remoteRead(() => this.remoteIds()) : this.localIds()));
|
|
387
|
+
}
|
|
367
388
|
async find(predicate) {
|
|
368
389
|
return this.run(true, () => this.mode === "remote-only" ? this.remoteRead(() => this.remote.find(predicate)) : this.local.find(predicate));
|
|
369
390
|
}
|
|
@@ -394,7 +415,12 @@ export class HybridStorage {
|
|
|
394
415
|
//
|
|
395
416
|
// ═══ Write Operations ═══
|
|
396
417
|
//
|
|
397
|
-
|
|
418
|
+
/**
|
|
419
|
+
* This method sets an item. The storage takes a copy of the value, so a later change of the
|
|
420
|
+
* value does not change the local copy, the queue or the remote write.
|
|
421
|
+
*/
|
|
422
|
+
async set(id, written) {
|
|
423
|
+
const value = structuredClone(written);
|
|
398
424
|
await this.write("set", { id, value }, [id], async () => {
|
|
399
425
|
this.bump(id);
|
|
400
426
|
await this.local.set(id, value);
|
|
@@ -415,7 +441,9 @@ export class HybridStorage {
|
|
|
415
441
|
//
|
|
416
442
|
// ═══ Bulk Operations ═══
|
|
417
443
|
//
|
|
418
|
-
|
|
444
|
+
/** This method sets several items. The storage takes a copy of each value, as `set` does. */
|
|
445
|
+
async setBatch(written) {
|
|
446
|
+
const items = written.map(([id, value]) => [id, structuredClone(value)]);
|
|
419
447
|
await this.write("setBatch", { items }, items.map(([id]) => id), async () => {
|
|
420
448
|
for (const [id] of items)
|
|
421
449
|
this.bump(id);
|
|
@@ -559,13 +587,9 @@ export class HybridStorage {
|
|
|
559
587
|
* - Syncs run one at a time.
|
|
560
588
|
*/
|
|
561
589
|
syncFromRemote() {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
throw this.loadError;
|
|
566
|
-
if (this.mode === "write-through" || this.mode === "write-back")
|
|
567
|
-
await this.pullRemote(false);
|
|
568
|
-
});
|
|
590
|
+
if (this.closed)
|
|
591
|
+
return Promise.reject(this.closedError());
|
|
592
|
+
return this.pull();
|
|
569
593
|
}
|
|
570
594
|
/**
|
|
571
595
|
* Sync all pending offline operations to remote.
|
|
@@ -574,13 +598,9 @@ export class HybridStorage {
|
|
|
574
598
|
* the call does nothing.
|
|
575
599
|
*/
|
|
576
600
|
syncToRemote() {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
throw this.loadError;
|
|
581
|
-
if (this.mode !== "local-only")
|
|
582
|
-
await this.pushQueue();
|
|
583
|
-
});
|
|
601
|
+
if (this.closed)
|
|
602
|
+
return Promise.reject(this.closedError());
|
|
603
|
+
return this.push();
|
|
584
604
|
}
|
|
585
605
|
/**
|
|
586
606
|
* This method sends the queue to the remote (roadmap R28). `ProcedureServer.useStorage` uses it. In
|
|
@@ -596,16 +616,52 @@ export class HybridStorage {
|
|
|
596
616
|
await this.syncToRemote();
|
|
597
617
|
await this.syncFromRemote();
|
|
598
618
|
}
|
|
619
|
+
/** The pull of `syncFromRemote()`, after the syncs that run. */
|
|
620
|
+
pull() {
|
|
621
|
+
return this.serialize(async () => {
|
|
622
|
+
await this.loaded;
|
|
623
|
+
if (this.loadError)
|
|
624
|
+
throw this.loadError;
|
|
625
|
+
if (this.mode === "write-through" || this.mode === "write-back")
|
|
626
|
+
await this.pullRemote(false);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
/** The push of `syncToRemote()`, after the syncs that run. */
|
|
630
|
+
push() {
|
|
631
|
+
return this.serialize(async () => {
|
|
632
|
+
await this.loaded;
|
|
633
|
+
if (this.loadError)
|
|
634
|
+
throw this.loadError;
|
|
635
|
+
if (this.mode !== "local-only")
|
|
636
|
+
await this.pushQueue();
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/** The error of an operation after `close()`. */
|
|
640
|
+
closedError() {
|
|
641
|
+
return new Error("The HybridStorage is closed");
|
|
642
|
+
}
|
|
599
643
|
//
|
|
600
644
|
// ═══ Lifecycle & Metadata ═══
|
|
601
645
|
//
|
|
602
646
|
/**
|
|
603
|
-
* This method
|
|
604
|
-
*
|
|
605
|
-
*
|
|
647
|
+
* This method closes the storage.
|
|
648
|
+
*
|
|
649
|
+
* - It stops the timers, and waits for the operations, the mode switch and the syncs that run.
|
|
650
|
+
* - It sends the queue (not in `local-only` mode). The queue store keeps the operations that
|
|
651
|
+
* the remote did not take.
|
|
652
|
+
* - It closes the remote and the local copy. A durable local copy keeps its items.
|
|
653
|
+
* - After it, each operation rejects. A second `close()` does nothing: it gives the promise of
|
|
654
|
+
* the first one, without its error.
|
|
606
655
|
*/
|
|
607
|
-
|
|
656
|
+
close() {
|
|
657
|
+
if (this.closing)
|
|
658
|
+
return this.closing.catch(() => undefined);
|
|
608
659
|
this.closed = true;
|
|
660
|
+
this.closing = this.shutDown();
|
|
661
|
+
return this.closing;
|
|
662
|
+
}
|
|
663
|
+
/** The work of `close()`. */
|
|
664
|
+
async shutDown() {
|
|
609
665
|
for (const timer of [this.syncTimer, this.pullTimer]) {
|
|
610
666
|
if (timer)
|
|
611
667
|
clearInterval(timer);
|
|
@@ -617,15 +673,20 @@ export class HybridStorage {
|
|
|
617
673
|
this.drainTimer = null;
|
|
618
674
|
}
|
|
619
675
|
await this.loaded;
|
|
620
|
-
//
|
|
621
|
-
|
|
676
|
+
// The operations and the switch that started before close() end first
|
|
677
|
+
await this.gate;
|
|
678
|
+
await this.idle();
|
|
679
|
+
// After the syncs that run: send the pending operations
|
|
680
|
+
await this.serialize(async () => {
|
|
681
|
+
if (this.loadError || this.offlineOps.length === 0 || this.mode === "local-only")
|
|
682
|
+
return;
|
|
622
683
|
try {
|
|
623
|
-
await this.
|
|
684
|
+
await this.pushQueue();
|
|
624
685
|
}
|
|
625
686
|
catch (error) {
|
|
626
687
|
console.error("Failed to sync before close:", error);
|
|
627
688
|
}
|
|
628
|
-
}
|
|
689
|
+
});
|
|
629
690
|
await this.storeChain;
|
|
630
691
|
// Close remote storage (an ApiStorage leaves the caller's client open)
|
|
631
692
|
try {
|
|
@@ -635,14 +696,22 @@ export class HybridStorage {
|
|
|
635
696
|
await this.local.close();
|
|
636
697
|
}
|
|
637
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* The metadata of the storage. `size` is the result of `size()`: the number of items of the
|
|
701
|
+
* storage, also in `remote-only` mode. `stats.localSize` is the number of items of the local
|
|
702
|
+
* copy. Before, `size` was the size of the local copy, which `remote-only` keeps empty (found by
|
|
703
|
+
* the contract suite of `@procedures/collections/testing`).
|
|
704
|
+
*/
|
|
638
705
|
async getMetadata() {
|
|
706
|
+
const currentSize = await this.size();
|
|
707
|
+
const localSize = await this.local.size();
|
|
639
708
|
const totalOps = this.stats.cacheHits + this.stats.cacheMisses;
|
|
640
709
|
const hitRate = totalOps > 0 ? (this.stats.cacheHits / totalOps) * 100 : 0;
|
|
641
|
-
const currentSize = await this.local.size();
|
|
642
710
|
return {
|
|
643
711
|
type: "hybrid",
|
|
644
712
|
size: currentSize,
|
|
645
713
|
stats: {
|
|
714
|
+
localSize,
|
|
646
715
|
hitRate: Math.round(hitRate * 100) / 100,
|
|
647
716
|
cacheHits: this.stats.cacheHits,
|
|
648
717
|
cacheMisses: this.stats.cacheMisses,
|
|
@@ -664,6 +733,8 @@ export class HybridStorage {
|
|
|
664
733
|
* and never during a mode switch.
|
|
665
734
|
*/
|
|
666
735
|
async run(read, work) {
|
|
736
|
+
if (this.closed)
|
|
737
|
+
throw this.closedError();
|
|
667
738
|
await (read ? this.ready : this.loaded);
|
|
668
739
|
for (;;) {
|
|
669
740
|
const gate = this.gate;
|
|
@@ -672,6 +743,9 @@ export class HybridStorage {
|
|
|
672
743
|
if (this.gate === gate)
|
|
673
744
|
break;
|
|
674
745
|
}
|
|
746
|
+
// close() started while this operation waited
|
|
747
|
+
if (this.closed)
|
|
748
|
+
throw this.closedError();
|
|
675
749
|
if (this.loadError)
|
|
676
750
|
throw this.loadError;
|
|
677
751
|
this.active++;
|
|
@@ -721,16 +795,17 @@ export class HybridStorage {
|
|
|
721
795
|
}
|
|
722
796
|
/** The first sync: send the loaded queue, then read the remote. */
|
|
723
797
|
async initialSync() {
|
|
724
|
-
|
|
798
|
+
// A close() before the first sync: the storage makes no remote call
|
|
799
|
+
if (this.loadError || this.closed)
|
|
725
800
|
return;
|
|
726
801
|
if (this.options.syncOnInit && this.mode !== "local-only") {
|
|
727
802
|
if (this.offlineOps.length > 0) {
|
|
728
|
-
await this.
|
|
803
|
+
await this.push().catch((err) => {
|
|
729
804
|
console.error("Failed to send the queue on init:", err);
|
|
730
805
|
});
|
|
731
806
|
}
|
|
732
|
-
if (this.mode === "write-through" || this.mode === "write-back") {
|
|
733
|
-
await this.
|
|
807
|
+
if (!this.closed && (this.mode === "write-through" || this.mode === "write-back")) {
|
|
808
|
+
await this.pull().catch((err) => {
|
|
734
809
|
console.error("Failed to sync on init:", err);
|
|
735
810
|
});
|
|
736
811
|
}
|
|
@@ -788,10 +863,9 @@ export class HybridStorage {
|
|
|
788
863
|
if (await this.pullItem(id, item, changedLocally, conflicts))
|
|
789
864
|
queueChanged = true;
|
|
790
865
|
}
|
|
791
|
-
// The remote deleted these items
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
: changes.deleted;
|
|
866
|
+
// The remote deleted these items. A full pull reads only the local ids: before, it read each
|
|
867
|
+
// local entry, and the local copy copied each value for it.
|
|
868
|
+
const gone = changes.full ? (await this.localIds()).filter((id) => !remoteIds.has(id)) : changes.deleted;
|
|
795
869
|
for (const id of gone) {
|
|
796
870
|
if (await this.pullDelete(id, changedLocally, conflicts))
|
|
797
871
|
queueChanged = true;
|
|
@@ -989,6 +1063,18 @@ export class HybridStorage {
|
|
|
989
1063
|
throw new MissingKeysError(missing);
|
|
990
1064
|
return entries;
|
|
991
1065
|
}
|
|
1066
|
+
/** The ids of the local copy: from `getIds()` when it has it, without a copy of the values. */
|
|
1067
|
+
async localIds() {
|
|
1068
|
+
if (this.local.getIds)
|
|
1069
|
+
return this.local.getIds();
|
|
1070
|
+
return (await this.local.getEntries()).map(([id]) => id);
|
|
1071
|
+
}
|
|
1072
|
+
/** The ids of the remote: from `getIds()` when it has it, or else from its entries. */
|
|
1073
|
+
async remoteIds() {
|
|
1074
|
+
if (this.remote.getIds)
|
|
1075
|
+
return this.remote.getIds();
|
|
1076
|
+
return (await this.remoteEntries()).map(([id]) => id);
|
|
1077
|
+
}
|
|
992
1078
|
/**
|
|
993
1079
|
* This method sends the queue. The operations that reached the remote leave the queue. The queue store
|
|
994
1080
|
* gets the change at the end.
|
|
@@ -1319,7 +1405,9 @@ export class HybridStorage {
|
|
|
1319
1405
|
const delay = this.options.syncInterval > 0 ? this.options.syncInterval : 5000;
|
|
1320
1406
|
this.drainTimer = setTimeout(() => {
|
|
1321
1407
|
this.drainTimer = null;
|
|
1322
|
-
|
|
1408
|
+
if (this.closed)
|
|
1409
|
+
return;
|
|
1410
|
+
this.push().catch(() => this.scheduleDrain());
|
|
1323
1411
|
}, delay);
|
|
1324
1412
|
this.drainTimer.unref?.();
|
|
1325
1413
|
}
|