@procedures/store 0.2.2 → 0.3.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 +63 -0
- package/README.md +109 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/indexeddb/idb.d.ts +100 -0
- package/dist/indexeddb/idb.d.ts.map +1 -0
- package/dist/indexeddb/idb.js +137 -0
- package/dist/indexeddb/idb.js.map +1 -0
- package/dist/indexeddb/program-store.d.ts +42 -0
- package/dist/indexeddb/program-store.d.ts.map +1 -0
- package/dist/indexeddb/program-store.js +71 -0
- package/dist/indexeddb/program-store.js.map +1 -0
- package/dist/indexeddb/queue-store.d.ts +38 -0
- package/dist/indexeddb/queue-store.d.ts.map +1 -0
- package/dist/indexeddb/queue-store.js +73 -0
- package/dist/indexeddb/queue-store.js.map +1 -0
- package/dist/indexeddb.d.ts +19 -0
- package/dist/indexeddb.d.ts.map +1 -0
- package/dist/indexeddb.js +17 -0
- package/dist/indexeddb.js.map +1 -0
- package/dist/program-store/file.d.ts +5 -0
- package/dist/program-store/file.d.ts.map +1 -1
- package/dist/program-store/file.js +5 -2
- package/dist/program-store/file.js.map +1 -1
- package/dist/program-store/procedures.d.ts.map +1 -1
- package/dist/program-store/procedures.js +5 -2
- package/dist/program-store/procedures.js.map +1 -1
- package/dist/storage/api.d.ts +18 -7
- package/dist/storage/api.d.ts.map +1 -1
- package/dist/storage/api.js +34 -3
- package/dist/storage/api.js.map +1 -1
- package/dist/storage/factory.d.ts +13 -2
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +12 -4
- package/dist/storage/factory.js.map +1 -1
- package/dist/storage/file-queue.d.ts +32 -0
- package/dist/storage/file-queue.d.ts.map +1 -0
- package/dist/storage/file-queue.js +101 -0
- package/dist/storage/file-queue.js.map +1 -0
- package/dist/storage/hybrid.d.ts +355 -42
- package/dist/storage/hybrid.d.ts.map +1 -1
- package/dist/storage/hybrid.js +1093 -250
- package/dist/storage/hybrid.js.map +1 -1
- package/dist/storage/index.d.ts +4 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +5 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/queue.d.ts +67 -0
- package/dist/storage/queue.d.ts.map +1 -0
- package/dist/storage/queue.js +85 -0
- package/dist/storage/queue.js.map +1 -0
- package/dist/storage/synced-registry.d.ts +18 -2
- package/dist/storage/synced-registry.d.ts.map +1 -1
- package/dist/storage/synced-registry.js +37 -3
- package/dist/storage/synced-registry.js.map +1 -1
- package/dist/storage/types.d.ts +5 -2
- package/dist/storage/types.d.ts.map +1 -1
- package/package.json +16 -6
- package/src/indexeddb/idb.ts +190 -0
- package/src/indexeddb/program-store.ts +80 -0
- package/src/indexeddb/queue-store.ts +80 -0
- package/src/indexeddb.ts +19 -0
- package/src/program-store/file.ts +5 -2
- package/src/program-store/procedures.ts +5 -2
- package/src/storage/api.ts +50 -10
- package/src/storage/factory.ts +21 -4
- package/src/storage/file-queue.ts +103 -0
- package/src/storage/hybrid.ts +1142 -274
- package/src/storage/index.ts +15 -2
- package/src/storage/queue.ts +128 -0
- package/src/storage/synced-registry.ts +49 -4
- package/src/storage/types.ts +6 -2
package/dist/storage/hybrid.d.ts
CHANGED
|
@@ -1,48 +1,76 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hybrid Storage Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* A local copy (an `InMemoryStorage`, or the `local` storage that the application gives) in
|
|
5
|
+
* front of a remote `CollectionStorage` (often an `ApiStorage`). The storage has four modes, and
|
|
6
|
+
* `setMode()` changes the mode while the storage operates (roadmap R28):
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
8
|
+
* | Mode | Reads | Writes | Remote calls |
|
|
9
|
+
* |---|---|---|---|
|
|
10
|
+
* | `local-only` | the local copy | the local copy and the queue | none |
|
|
11
|
+
* | `write-through` | the local copy, the remote on a miss | the local copy, then the remote | yes |
|
|
12
|
+
* | `write-back` | the local copy, the remote on a miss | the local copy and the queue, a timer sends it | yes |
|
|
13
|
+
* | `remote-only` | the remote | the remote | yes, and no local copy |
|
|
14
|
+
*
|
|
15
|
+
* - The queue is durable through the `queueStore` that the application gives.
|
|
16
|
+
* - A sync asks for the changes since a version when the remote has `getChangesSince`.
|
|
17
|
+
* - With `versionOf`, a conflict is a change on both sides, and `conflictResolution` decides.
|
|
18
|
+
* - `pullInterval` keeps the local copy fresh.
|
|
13
19
|
*/
|
|
14
20
|
import type { CollectionStorage, StorageMetadata } from "@procedures/collections";
|
|
21
|
+
import { type Duration } from "@procedures/client";
|
|
22
|
+
import { type QueueStore } from "./queue.js";
|
|
15
23
|
/**
|
|
16
24
|
* Conflict resolution strategy.
|
|
17
25
|
*/
|
|
18
26
|
export type ConflictResolution = "local" | "remote" | "merge" | "error";
|
|
19
27
|
/**
|
|
20
28
|
* Write strategy for hybrid storage.
|
|
29
|
+
* @deprecated Use `HybridMode`: the strategies are two of its modes.
|
|
21
30
|
*/
|
|
22
31
|
export type WriteStrategy = "write-through" | "write-back";
|
|
32
|
+
/**
|
|
33
|
+
* The mode of a `HybridStorage` (roadmap R28).
|
|
34
|
+
*
|
|
35
|
+
* - `local-only`: the storage makes no remote call. Each write goes to the local copy and to the
|
|
36
|
+
* queue, so a later switch to a remote mode sends it.
|
|
37
|
+
* - `write-through`: each write goes to the local copy, then to the remote.
|
|
38
|
+
* - `write-back`: each write goes to the local copy and to the queue. A timer sends the queue.
|
|
39
|
+
* - `remote-only`: each read and each write goes to the remote. The storage keeps no local copy.
|
|
40
|
+
*/
|
|
41
|
+
export type HybridMode = "local-only" | "write-through" | "write-back" | "remote-only";
|
|
23
42
|
/**
|
|
24
43
|
* Hybrid storage configuration options.
|
|
25
44
|
*/
|
|
26
45
|
export interface HybridStorageOptions {
|
|
27
46
|
/**
|
|
28
|
-
*
|
|
29
|
-
* - write-through: Write to both local and remote immediately
|
|
30
|
-
* - write-back: Write to local immediately, sync to remote later
|
|
47
|
+
* The first mode. `setMode()` changes it later.
|
|
31
48
|
* @default "write-through"
|
|
32
49
|
*/
|
|
50
|
+
mode?: HybridMode;
|
|
51
|
+
/**
|
|
52
|
+
* The first mode, with its old name. `mode` comes first when both are given.
|
|
53
|
+
* @deprecated Use `mode`.
|
|
54
|
+
*/
|
|
33
55
|
writeStrategy?: WriteStrategy;
|
|
34
56
|
/**
|
|
35
57
|
* Conflict resolution strategy
|
|
36
58
|
* - local: Local changes win
|
|
37
59
|
* - remote: Remote changes win
|
|
38
60
|
* - merge: Custom merge function (requires mergeFn)
|
|
39
|
-
* - error:
|
|
61
|
+
* - error: The sync throws `ConflictError` with the ids, and changes nothing for them
|
|
62
|
+
*
|
|
63
|
+
* With `versionOf`, a conflict is a queued local write of an id whose remote version changed
|
|
64
|
+
* after the last version that the storage saw. A queued write of an id without a known remote
|
|
65
|
+
* version wins. Without `versionOf`, a conflict is a local value that differs from the remote
|
|
66
|
+
* value, with no queued write: a queued write always wins.
|
|
40
67
|
* @default "remote"
|
|
41
68
|
*/
|
|
42
69
|
conflictResolution?: ConflictResolution;
|
|
43
70
|
/**
|
|
44
71
|
* Custom merge function for conflict resolution.
|
|
45
|
-
* Only used when conflictResolution is "merge".
|
|
72
|
+
* Only used when conflictResolution is "merge". A conflict where one side deleted the item
|
|
73
|
+
* keeps the local side.
|
|
46
74
|
*
|
|
47
75
|
* @param local - Local version
|
|
48
76
|
* @param remote - Remote version
|
|
@@ -55,28 +83,126 @@ export interface HybridStorageOptions {
|
|
|
55
83
|
*/
|
|
56
84
|
keyOf?: (item: any) => string | undefined;
|
|
57
85
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
86
|
+
* The version of an item, for example its ETag or a revision field (roadmap R28). The sync
|
|
87
|
+
* compares the versions, not the JSON of the values. A remote change of an id with a queued
|
|
88
|
+
* local write is a conflict.
|
|
61
89
|
*/
|
|
62
|
-
|
|
90
|
+
versionOf?: (item: any) => string | undefined;
|
|
63
91
|
/**
|
|
64
|
-
*
|
|
92
|
+
* Automatic sync interval (for write-back mode), for example `seconds(5)`.
|
|
93
|
+
* Set to 0 to disable automatic sync. In write-through mode, it is also the delay before the
|
|
94
|
+
* storage sends the queue after a failure (5 seconds when it is 0).
|
|
95
|
+
* @default 5 seconds
|
|
96
|
+
*/
|
|
97
|
+
syncInterval?: Duration;
|
|
98
|
+
/**
|
|
99
|
+
* The interval of the periodic pull, for example `minutes(1)` (roadmap R28). A pull reads the
|
|
100
|
+
* remote changes into the local copy in the modes `write-through` and `write-back`.
|
|
101
|
+
* @default no periodic pull
|
|
102
|
+
*/
|
|
103
|
+
pullInterval?: Duration;
|
|
104
|
+
/**
|
|
105
|
+
* Initialize by syncing from remote on construction: send the loaded queue, then read the
|
|
106
|
+
* remote. The mode `local-only` makes no remote call.
|
|
65
107
|
* @default true
|
|
66
108
|
*/
|
|
67
109
|
syncOnInit?: boolean;
|
|
68
110
|
/**
|
|
69
|
-
* Maximum offline operations to queue.
|
|
111
|
+
* Maximum offline operations to queue. A full queue refuses the next write.
|
|
70
112
|
* @default 1000
|
|
71
113
|
*/
|
|
72
114
|
maxOfflineOps?: number;
|
|
73
115
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
116
|
+
* The queue of the write-through mode.
|
|
117
|
+
* In write-through mode, a write that the remote cannot take stays in the local cache and the
|
|
118
|
+
* queue, and the write throws `PendingWriteError`. A timer sends the queue later.
|
|
119
|
+
* Without the queue, the write is undone in the local cache and the write throws.
|
|
120
|
+
* The modes `write-back` and `local-only` always use the queue.
|
|
76
121
|
* @default true
|
|
77
122
|
*/
|
|
78
123
|
offlineQueue?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* The store that keeps the queue (roadmap R28). A new storage with the same store loads the
|
|
126
|
+
* queue again and sends it. `FileQueueStore` of `@procedures/store/file-queue` keeps it in a
|
|
127
|
+
* file.
|
|
128
|
+
* @default a new `MemoryQueueStore`
|
|
129
|
+
*/
|
|
130
|
+
queueStore?: QueueStore;
|
|
131
|
+
/**
|
|
132
|
+
* True when an error of the remote means "not available now", so the write can wait in the
|
|
133
|
+
* queue. Another error (a refusal of the remote) undoes the write. The default: an error with
|
|
134
|
+
* `retryable: true`, or an error without a `code`.
|
|
135
|
+
*/
|
|
136
|
+
isUnavailable?: (error: unknown) => boolean;
|
|
137
|
+
/**
|
|
138
|
+
* The storage of the local copy (roadmap R45). A durable storage, for example `SqliteStorage`
|
|
139
|
+
* of `@procedures/sqlite`, keeps the local copy after a restart. Then a new storage in
|
|
140
|
+
* `local-only` mode reads the items of the last run. Give it with a durable `queueStore`.
|
|
141
|
+
* `close()` closes it.
|
|
142
|
+
* @default a new `InMemoryStorage`
|
|
143
|
+
*/
|
|
144
|
+
local?: LocalCopyStorage<any>;
|
|
79
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* The remote did not take a write of the write-through mode. The write is in the local cache and
|
|
148
|
+
* in the queue, and the storage sends it later (roadmap R2). `cause` is the error of the remote.
|
|
149
|
+
*/
|
|
150
|
+
export declare class PendingWriteError extends Error {
|
|
151
|
+
readonly name = "PendingWriteError";
|
|
152
|
+
readonly code = "WRITE_PENDING";
|
|
153
|
+
readonly pending = true;
|
|
154
|
+
constructor(operation: string, cause: unknown);
|
|
155
|
+
}
|
|
156
|
+
/** The queue of operations is full, so the storage refuses the write (roadmap R2). */
|
|
157
|
+
export declare class QueueFullError extends Error {
|
|
158
|
+
readonly name = "QueueFullError";
|
|
159
|
+
readonly code = "QUEUE_FULL";
|
|
160
|
+
constructor(max: number);
|
|
161
|
+
}
|
|
162
|
+
/** The remote gave items without a key, so a sync cannot know which local items to keep. */
|
|
163
|
+
export declare class MissingKeysError extends Error {
|
|
164
|
+
readonly name = "MissingKeysError";
|
|
165
|
+
readonly code = "MISSING_KEYS";
|
|
166
|
+
constructor(count: number);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The queue store failed (roadmap R28). A write that the store did not keep is undone and not
|
|
170
|
+
* acknowledged. A storage whose store cannot load refuses each operation.
|
|
171
|
+
*/
|
|
172
|
+
export declare class QueueStoreError extends Error {
|
|
173
|
+
readonly name = "QueueStoreError";
|
|
174
|
+
readonly code = "QUEUE_STORE";
|
|
175
|
+
constructor(action: "load" | "write", cause: unknown);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The sync found conflicts and `conflictResolution` is "error" (roadmap R28). The local values and
|
|
179
|
+
* their queued writes stay. `ids` are the ids of the conflicts.
|
|
180
|
+
*/
|
|
181
|
+
export declare class ConflictError extends Error {
|
|
182
|
+
readonly ids: string[];
|
|
183
|
+
readonly name = "ConflictError";
|
|
184
|
+
readonly code = "SYNC_CONFLICT";
|
|
185
|
+
constructor(ids: string[]);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* `setMode()` could not change the mode (roadmap R28). The mode did not change. `cause` is the
|
|
189
|
+
* error of the flush or of the sync.
|
|
190
|
+
*/
|
|
191
|
+
export declare class ModeSwitchError extends Error {
|
|
192
|
+
readonly from: HybridMode;
|
|
193
|
+
readonly to: HybridMode;
|
|
194
|
+
readonly name = "ModeSwitchError";
|
|
195
|
+
readonly code = "MODE_SWITCH_FAILED";
|
|
196
|
+
constructor(from: HybridMode, to: HybridMode, cause: unknown);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* A storage for the local copy of a `HybridStorage` (roadmap R45): a collection storage that gives
|
|
200
|
+
* its entries. A durable one, for example `SqliteStorage` of `@procedures/sqlite`, keeps the local
|
|
201
|
+
* copy after a restart.
|
|
202
|
+
*/
|
|
203
|
+
export type LocalCopyStorage<T> = CollectionStorage<T> & {
|
|
204
|
+
getEntries(): Promise<Array<[string, T]>>;
|
|
205
|
+
};
|
|
80
206
|
/**
|
|
81
207
|
* Hybrid storage combining local cache with remote persistence.
|
|
82
208
|
*
|
|
@@ -88,9 +214,9 @@ export interface HybridStorageOptions {
|
|
|
88
214
|
*
|
|
89
215
|
* const remote = new ApiStorage(client, { collection: "users" });
|
|
90
216
|
* const hybrid = new HybridStorage(remote, {
|
|
91
|
-
*
|
|
217
|
+
* mode: "write-through",
|
|
92
218
|
* conflictResolution: "remote",
|
|
93
|
-
* syncInterval:
|
|
219
|
+
* syncInterval: seconds(5)
|
|
94
220
|
* });
|
|
95
221
|
*
|
|
96
222
|
* // Fast local reads
|
|
@@ -98,44 +224,136 @@ export interface HybridStorageOptions {
|
|
|
98
224
|
*
|
|
99
225
|
* // Writes go to both local and remote
|
|
100
226
|
* await hybrid.set("123", { name: "John" });
|
|
227
|
+
*
|
|
228
|
+
* // Offline: no remote call. The writes wait in the queue.
|
|
229
|
+
* await hybrid.setMode("local-only");
|
|
230
|
+
* // Online again: the switch sends the queue, then syncs the local copy
|
|
231
|
+
* await hybrid.setMode("write-through");
|
|
101
232
|
* ```
|
|
102
233
|
*/
|
|
103
234
|
export declare class HybridStorage<T> implements CollectionStorage<T> {
|
|
104
|
-
private local;
|
|
105
|
-
private remote;
|
|
235
|
+
private readonly local;
|
|
236
|
+
private readonly remote;
|
|
237
|
+
private readonly queueStore;
|
|
106
238
|
private options;
|
|
239
|
+
private mode;
|
|
107
240
|
private offlineOps;
|
|
241
|
+
/** The operations in `offlineOps` that the queue store does not have yet */
|
|
242
|
+
private readonly unpersisted;
|
|
243
|
+
/** True when the queue store can differ from `offlineOps`: a failed write, or sent operations */
|
|
244
|
+
private storeDirty;
|
|
245
|
+
/** The calls of the queue store, one at a time */
|
|
246
|
+
private storeChain;
|
|
247
|
+
/** The push timer of the write-back mode */
|
|
108
248
|
private syncTimer;
|
|
249
|
+
/** The periodic pull */
|
|
250
|
+
private pullTimer;
|
|
251
|
+
/** True while pushQueue sends the queue: the first operation can be on its way */
|
|
252
|
+
private pushing;
|
|
253
|
+
/** The timer that sends the queue of the write-through mode after a failure */
|
|
254
|
+
private drainTimer;
|
|
255
|
+
/** The load of the queue. The writes wait for it. */
|
|
256
|
+
private readonly loaded;
|
|
257
|
+
/** The load and the first sync. The reads wait for it, so a read never sees an empty cache by mistake. */
|
|
258
|
+
private readonly ready;
|
|
259
|
+
/** The error of the load (of the queue store, or of the local copy): the storage refuses each operation */
|
|
260
|
+
private loadError;
|
|
261
|
+
/** A mode switch that runs. The operations wait for it. */
|
|
262
|
+
private gate;
|
|
263
|
+
/** The operations that run, and the switches that wait for them to end */
|
|
264
|
+
private active;
|
|
265
|
+
private idleWaiters;
|
|
109
266
|
private isOnline;
|
|
267
|
+
private closed;
|
|
110
268
|
/** A counter of local writes for each id, so a sync does not restore an older remote value */
|
|
111
269
|
private readonly writeVersions;
|
|
112
270
|
private clearVersion;
|
|
113
271
|
/** The running sync: syncs run one at a time */
|
|
114
272
|
private syncChain;
|
|
273
|
+
/** The remote version of the last delta pull */
|
|
274
|
+
private cursor;
|
|
275
|
+
/** True when the remote has `getChangesSince` but its server does not know `changes` */
|
|
276
|
+
private changesUnsupported;
|
|
277
|
+
/** With `versionOf`: the last known remote version of each id. `null`: the remote has no item. */
|
|
278
|
+
private readonly bases;
|
|
115
279
|
private stats;
|
|
116
280
|
constructor(remote: CollectionStorage<T>, options?: HybridStorageOptions);
|
|
281
|
+
/** The mode of the storage now. */
|
|
282
|
+
getMode(): HybridMode;
|
|
283
|
+
/**
|
|
284
|
+
* This method changes the mode while the storage operates (roadmap R28).
|
|
285
|
+
*
|
|
286
|
+
* - The switch waits for the operations that run. The operations that start later wait for
|
|
287
|
+
* the switch. Switches and syncs run one at a time.
|
|
288
|
+
* - A switch to a remote mode sends the queue first. A switch to `local-only` makes no remote
|
|
289
|
+
* call: the queue stays until the next remote mode.
|
|
290
|
+
* - A switch to `remote-only` drops the local copy, after the queue reached the remote.
|
|
291
|
+
* - A switch from `local-only` or `remote-only` to a mode with a local copy syncs the local
|
|
292
|
+
* copy. The reads wait for the sync.
|
|
293
|
+
* - When the flush or the sync fails, the mode does not change, and the switch throws
|
|
294
|
+
* `ModeSwitchError`.
|
|
295
|
+
*/
|
|
296
|
+
setMode(mode: HybridMode): Promise<void>;
|
|
297
|
+
private switchTo;
|
|
298
|
+
/** This method drops the local copy. A read in flight does not fill it again. */
|
|
299
|
+
private dropLocal;
|
|
300
|
+
/** This method makes the local copy from the queue only, before a full sync. */
|
|
301
|
+
private rebuildLocal;
|
|
302
|
+
/** This method starts and stops the timers for the mode. */
|
|
303
|
+
private updateTimers;
|
|
117
304
|
/**
|
|
118
305
|
* The local item, or else the remote item (which the cache then keeps). When the remote
|
|
119
|
-
* cannot answer, the storage is offline and the result is undefined.
|
|
306
|
+
* cannot answer, the storage is offline and the result is undefined. In `local-only` mode, a
|
|
307
|
+
* miss is undefined. In `remote-only` mode, the remote answers, and its error passes through.
|
|
120
308
|
*/
|
|
121
309
|
get(id: string): Promise<T | undefined>;
|
|
122
310
|
getAll(): Promise<T[]>;
|
|
311
|
+
/** Every item with its id. */
|
|
312
|
+
getEntries(): Promise<Array<[string, T]>>;
|
|
123
313
|
find(predicate: (item: T) => boolean): Promise<T[]>;
|
|
124
314
|
has(id: string): Promise<boolean>;
|
|
125
315
|
size(): Promise<number>;
|
|
316
|
+
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
317
|
+
/** A read of the remote-only mode. A queue that a restart loaded reaches the remote first. */
|
|
318
|
+
private remoteRead;
|
|
126
319
|
set(id: string, value: T): Promise<void>;
|
|
127
320
|
delete(id: string): Promise<boolean>;
|
|
128
321
|
clear(): Promise<void>;
|
|
129
322
|
setBatch(items: Array<[string, T]>): Promise<void>;
|
|
130
323
|
deleteBatch(ids: string[]): Promise<number>;
|
|
131
|
-
getBatch(ids: string[]): Promise<Map<string, T>>;
|
|
132
324
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
325
|
+
* One write, in the mode of the storage. The result is the result of the local copy, or of the
|
|
326
|
+
* remote in `remote-only` mode.
|
|
327
|
+
*
|
|
328
|
+
* - `remote-only`: the remote only. Its error passes through.
|
|
329
|
+
* - `write-back` and `local-only`: the local copy and the queue. A full queue refuses the write
|
|
330
|
+
* before the local change. The queue store keeps the operation before the write ends.
|
|
331
|
+
* - `write-through`, the remote refuses the write: the local change is undone, and the error
|
|
332
|
+
* of the remote passes through.
|
|
333
|
+
* - `write-through`, the remote is not available: with the queue, the write waits in it, a timer
|
|
334
|
+
* sends it later, and the write throws `PendingWriteError`. Without the queue, or with a full
|
|
335
|
+
* queue, the local change is undone.
|
|
135
336
|
*
|
|
136
|
-
*
|
|
337
|
+
* Before, a failed write-through stayed in the local cache and in the queue, no timer sent the
|
|
338
|
+
* queue, a second try queued the operation again, and a full queue dropped the oldest operation
|
|
339
|
+
* without a word (roadmap R2).
|
|
340
|
+
*/
|
|
341
|
+
private write;
|
|
342
|
+
/**
|
|
343
|
+
* The local values of the ids before a write, and a function that puts them back. It puts back
|
|
344
|
+
* only the ids that no later write changed.
|
|
345
|
+
*/
|
|
346
|
+
private snapshot;
|
|
347
|
+
/**
|
|
348
|
+
* Sync from the remote to the local copy, in the modes `write-through` and `write-back`. The
|
|
349
|
+
* other modes have no local copy to sync (`remote-only`) or make no remote call (`local-only`):
|
|
350
|
+
* the call does nothing.
|
|
351
|
+
*
|
|
352
|
+
* - The sync asks for the changes since the version of the last sync when the remote has
|
|
353
|
+
* `getChangesSince`. Else it reads all entries.
|
|
354
|
+
* - Each remote item goes into the cache under its id.
|
|
137
355
|
* - A local item that the remote does not have is removed, unless a queued local operation
|
|
138
|
-
* changes it
|
|
356
|
+
* changes it.
|
|
139
357
|
* - An id that a local write changed during the sync keeps the local value: the remote value
|
|
140
358
|
* is older.
|
|
141
359
|
* - Syncs run one at a time.
|
|
@@ -144,43 +362,138 @@ export declare class HybridStorage<T> implements CollectionStorage<T> {
|
|
|
144
362
|
/**
|
|
145
363
|
* Sync all pending offline operations to remote.
|
|
146
364
|
* Processes queued operations in order. When one fails, it and the later operations stay in
|
|
147
|
-
* the queue: the operations that reached the remote are not sent again.
|
|
365
|
+
* the queue: the operations that reached the remote are not sent again. In `local-only` mode,
|
|
366
|
+
* the call does nothing.
|
|
148
367
|
*/
|
|
149
368
|
syncToRemote(): Promise<void>;
|
|
369
|
+
/**
|
|
370
|
+
* This method sends the queue to the remote (roadmap R28). `ProcedureServer.useStorage` uses it. In
|
|
371
|
+
* `local-only` mode, it sends nothing: the queue waits for a remote mode.
|
|
372
|
+
*/
|
|
373
|
+
flush(): Promise<void>;
|
|
150
374
|
/**
|
|
151
375
|
* Force a full sync (both directions).
|
|
152
376
|
*/
|
|
153
377
|
sync(): Promise<void>;
|
|
378
|
+
/**
|
|
379
|
+
* This method stops the timers, sends the queue (not in `local-only` mode), and closes the remote and the
|
|
380
|
+
* local copy. The queue store keeps the operations that the remote did not take. A durable local
|
|
381
|
+
* copy keeps its items.
|
|
382
|
+
*/
|
|
154
383
|
close(): Promise<void>;
|
|
155
384
|
getMetadata(): Promise<StorageMetadata>;
|
|
385
|
+
/**
|
|
386
|
+
* This method starts a public operation: after the load of the queue (a write) or the first sync (a read),
|
|
387
|
+
* and never during a mode switch.
|
|
388
|
+
*/
|
|
389
|
+
private run;
|
|
390
|
+
/** A promise that resolves when no operation runs. */
|
|
391
|
+
private idle;
|
|
392
|
+
/** This method loads the queue, and puts its writes in the local copy. */
|
|
393
|
+
private loadQueue;
|
|
394
|
+
/** The first sync: send the loaded queue, then read the remote. */
|
|
395
|
+
private initialSync;
|
|
396
|
+
/** This method applies a queued operation to the local copy. */
|
|
397
|
+
private applyToLocal;
|
|
398
|
+
/**
|
|
399
|
+
* One pull. `full`: ask for all items (after the local copy was dropped).
|
|
400
|
+
*
|
|
401
|
+
* - The versions of the local writes at the start: a write during the pull keeps its value.
|
|
402
|
+
* - The cursor moves only after a pull without conflicts, so a conflict of the "error" policy
|
|
403
|
+
* comes again at the next pull.
|
|
404
|
+
*/
|
|
156
405
|
private pullRemote;
|
|
406
|
+
/**
|
|
407
|
+
* The remote changes: since the cursor from `getChangesSince` when the remote has it, or else
|
|
408
|
+
* all entries.
|
|
409
|
+
*/
|
|
410
|
+
private fetchChanges;
|
|
411
|
+
/** This method applies one remote item. It gives true when the queue changed. */
|
|
412
|
+
private pullItem;
|
|
413
|
+
/** This method applies one remote delete. It gives true when the queue changed. */
|
|
414
|
+
private pullDelete;
|
|
415
|
+
/**
|
|
416
|
+
* A queued local write of an id whose remote version changed: a conflict, and the policy
|
|
417
|
+
* decides. True when the queue changed.
|
|
418
|
+
*/
|
|
419
|
+
private resolvePending;
|
|
420
|
+
/**
|
|
421
|
+
* This method removes an id from the queued operations. It gives false when a queued clear touches it: nothing
|
|
422
|
+
* changes then.
|
|
423
|
+
*/
|
|
424
|
+
private dropQueued;
|
|
425
|
+
/**
|
|
426
|
+
* The remote items with their ids: from `getEntries()` when the remote has it, or else from
|
|
427
|
+
* `getAll()` and `keyOf`. An item without a key stops the sync before any change. Before, the
|
|
428
|
+
* sync skipped such items and then deleted each local item that the remote list did not have,
|
|
429
|
+
* so the remote list without ids emptied the cache (roadmap R2).
|
|
430
|
+
*/
|
|
431
|
+
private remoteEntries;
|
|
432
|
+
/**
|
|
433
|
+
* This method sends the queue. The operations that reached the remote leave the queue. The queue store
|
|
434
|
+
* gets the change at the end.
|
|
435
|
+
*/
|
|
157
436
|
private pushQueue;
|
|
158
437
|
/** Run syncs one at a time. */
|
|
159
438
|
private serialize;
|
|
160
|
-
private localIds;
|
|
161
439
|
private bump;
|
|
162
|
-
|
|
440
|
+
/** The count of local writes of an id: it changes with each write and each clear. */
|
|
441
|
+
private writeKey;
|
|
442
|
+
private versionOfItem;
|
|
443
|
+
/** This method records the remote version of an id that the storage read. */
|
|
444
|
+
private noteBase;
|
|
445
|
+
/** This method records the remote versions after a write reached the remote. */
|
|
446
|
+
private noteRemoteWrite;
|
|
447
|
+
/**
|
|
448
|
+
* This method puts in the operation the remote version that it changes, for each id: the version that an
|
|
449
|
+
* older queued operation of the id writes, or else the known remote version. A storage that
|
|
450
|
+
* loads the queue after a restart takes the version of the first operation of each id.
|
|
451
|
+
*/
|
|
452
|
+
private attachBase;
|
|
453
|
+
/** The remote version of an id after the queued operations, or undefined when it is not known. */
|
|
454
|
+
private expectedBase;
|
|
163
455
|
private hasPendingOp;
|
|
164
456
|
/**
|
|
165
457
|
* Resolve conflict between local and remote versions.
|
|
166
458
|
*/
|
|
167
459
|
private resolveConflict;
|
|
168
460
|
/**
|
|
169
|
-
*
|
|
461
|
+
* This method puts an operation in the queue, then in the queue store. The write ends only after the store
|
|
462
|
+
* kept it. When the store fails, the operation leaves the queue, the local change is undone,
|
|
463
|
+
* and the write throws `QueueStoreError`.
|
|
464
|
+
*
|
|
465
|
+
* A set or a delete of one id replaces the queued set or delete of that id, when no later
|
|
466
|
+
* operation touches the id: a second try of a write does not queue it two times.
|
|
170
467
|
*/
|
|
171
|
-
private
|
|
468
|
+
private enqueue;
|
|
172
469
|
/**
|
|
173
|
-
*
|
|
470
|
+
* This method writes one operation to the queue store: an append when the store holds each operation
|
|
471
|
+
* before it, or else the whole queue. On a failure, `rollback` removes the operation from the
|
|
472
|
+
* queue before the next call of the store.
|
|
174
473
|
*/
|
|
175
|
-
private
|
|
474
|
+
private persist;
|
|
475
|
+
/** This method writes the whole queue to the queue store when the store can differ from the queue. */
|
|
476
|
+
private persistAll;
|
|
176
477
|
/**
|
|
177
|
-
*
|
|
478
|
+
* Before a write goes to the remote directly, the queue store must not hold a sent operation:
|
|
479
|
+
* after a restart, it would replace this newer write.
|
|
178
480
|
*/
|
|
179
|
-
private
|
|
481
|
+
private ensureStoreClean;
|
|
482
|
+
/** The index of the queued operation that `op` replaces, or -1. */
|
|
483
|
+
private replaceableIndex;
|
|
484
|
+
/** True when the queue can take the operation. */
|
|
485
|
+
private hasQueueRoom;
|
|
486
|
+
/** A full queue refuses the write. Before, it dropped the oldest operation (roadmap R2). */
|
|
487
|
+
private ensureQueueRoom;
|
|
180
488
|
/**
|
|
181
|
-
*
|
|
489
|
+
* In write-through mode, a timer sends the queue after a failure, and again after each failed
|
|
490
|
+
* send, until the queue is empty.
|
|
182
491
|
*/
|
|
183
|
-
private
|
|
492
|
+
private scheduleDrain;
|
|
493
|
+
/**
|
|
494
|
+
* Execute a queued operation against remote storage.
|
|
495
|
+
*/
|
|
496
|
+
private executeOperation;
|
|
184
497
|
/**
|
|
185
498
|
* Get current online status.
|
|
186
499
|
*/
|
|
@@ -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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"}
|