@ebbjs/storage 0.2.0 → 1.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/dist/indexeddb/index.d.ts +23 -5
- package/dist/indexeddb/index.js +153 -148
- package/dist/memory/index.d.ts +23 -5
- package/dist/memory/index.js +35 -38
- package/dist/replica/index.d.ts +10 -1
- package/dist/replica/index.js +102 -100
- package/dist/{replica-record-D3lkFACh.js → replica-record-D5gGEoVQ.js} +59 -47
- package/dist/types/index.d.ts +41 -6
- package/package.json +2 -2
|
@@ -229,7 +229,8 @@ interface CursorStore {
|
|
|
229
229
|
* `conflicted` is a terminal-until-resolved hold: a catch-up pass
|
|
230
230
|
* classified the Action as a loser, so it is structurally excluded from
|
|
231
231
|
* `pending()` and never submitted. The application resolves it by
|
|
232
|
-
*
|
|
232
|
+
* sending the Action unchanged (back to `pending`) or clearing it
|
|
233
|
+
* (removed).
|
|
233
234
|
*/
|
|
234
235
|
type OutboxStatus = "pending" | "acknowledged" | "error" | "conflicted";
|
|
235
236
|
/**
|
|
@@ -267,6 +268,12 @@ interface ConflictSlot {
|
|
|
267
268
|
/** The value the server holds at the position after the missed window. */
|
|
268
269
|
readonly remote: ConflictSide;
|
|
269
270
|
}
|
|
271
|
+
/** One missed Action that out-dated a buffered write and made it a conflict. */
|
|
272
|
+
interface ConflictRemoteAction {
|
|
273
|
+
readonly actorId: string;
|
|
274
|
+
readonly actionId: string;
|
|
275
|
+
readonly gsn: number;
|
|
276
|
+
}
|
|
270
277
|
/**
|
|
271
278
|
* The before/after entity rows for one losing subject. `before` is the
|
|
272
279
|
* row as the client had it immediately before the catch-up window was
|
|
@@ -305,6 +312,18 @@ interface OutboxConflict {
|
|
|
305
312
|
* application reads that as "nothing of mine would be lost".
|
|
306
313
|
*/
|
|
307
314
|
readonly slots: readonly ConflictSlot[];
|
|
315
|
+
/**
|
|
316
|
+
* The looser of the losing subjects' declared scopes: `"entity"` when
|
|
317
|
+
* any losing subject declared entity scope, else `"field"`. The union is
|
|
318
|
+
* spelled inline so storage stays independent of the schema package.
|
|
319
|
+
*/
|
|
320
|
+
readonly scope: "field" | "component" | "entity";
|
|
321
|
+
/**
|
|
322
|
+
* The concurrent missed Actions that touched the losing subjects,
|
|
323
|
+
* deduped by action id, in window order. The remote half of the
|
|
324
|
+
* report an application builds a policy from.
|
|
325
|
+
*/
|
|
326
|
+
readonly remoteActions: readonly ConflictRemoteAction[];
|
|
308
327
|
/** The catch-up pass that produced this entry. */
|
|
309
328
|
readonly catchUpId: string;
|
|
310
329
|
/**
|
|
@@ -353,10 +372,9 @@ interface OutboxEntry {
|
|
|
353
372
|
interface OutboxStore {
|
|
354
373
|
put(entry: OutboxEntry): Promise<void>;
|
|
355
374
|
/**
|
|
356
|
-
* Persist `entry` and delete `deleteId` in one durable operation
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* without the re-assert persisted.
|
|
375
|
+
* Persist `entry` and delete `deleteId` in one durable operation: the
|
|
376
|
+
* replacement must never exist without the replaced row being gone,
|
|
377
|
+
* nor the row be gone without the replacement persisted.
|
|
360
378
|
*/
|
|
361
379
|
putAndDelete(entry: OutboxEntry, deleteId: string): Promise<void>;
|
|
362
380
|
list(): Promise<readonly OutboxEntry[]>;
|
package/dist/indexeddb/index.js
CHANGED
|
@@ -1,113 +1,117 @@
|
|
|
1
|
-
import { unwrap as
|
|
2
|
-
import { m as
|
|
3
|
-
import { c as
|
|
4
|
-
import { compare as
|
|
5
|
-
const
|
|
1
|
+
import { unwrap as h, openDB as D } from "idb";
|
|
2
|
+
import { m as _, a as T, i as O, c as v } from "../cursor-record-D39emd6R.js";
|
|
3
|
+
import { c as R, l as b, f as k, a as N, b as P, j as C, k as B, q as M, t as F, u as H, M as g, m as L, s as x, r as G, i as K } from "../replica-record-D5gGEoVQ.js";
|
|
4
|
+
import { compare as U } from "@ebbjs/core";
|
|
5
|
+
const Y = 10, V = 9, w = "actionPosition", j = "replica", z = (t, e, a, o) => {
|
|
6
|
+
const s = e > 0 && e < V;
|
|
7
|
+
if (s)
|
|
8
|
+
for (const r of Array.from(t.objectStoreNames))
|
|
9
|
+
t.deleteObjectStore(r);
|
|
6
10
|
if (t.objectStoreNames.contains("actions"))
|
|
7
|
-
|
|
11
|
+
o.objectStore("actions").indexNames.contains("position") || o.objectStore("actions").createIndex("position", "position");
|
|
8
12
|
else {
|
|
9
|
-
const
|
|
10
|
-
|
|
13
|
+
const r = t.createObjectStore("actions", { keyPath: "id" });
|
|
14
|
+
r.createIndex("subject_id", "subject_ids", { multiEntry: !0 }), r.createIndex("position", "position");
|
|
11
15
|
}
|
|
12
16
|
if (!t.objectStoreNames.contains("entities")) {
|
|
13
|
-
const
|
|
14
|
-
|
|
17
|
+
const r = t.createObjectStore("entities", { keyPath: "id" });
|
|
18
|
+
r.indexNames.contains("type") || r.createIndex("type", "type");
|
|
15
19
|
}
|
|
16
20
|
if (t.objectStoreNames.contains("relationships") || t.createObjectStore("relationships", { keyPath: "key" }), !t.objectStoreNames.contains("dirty")) {
|
|
17
|
-
const
|
|
18
|
-
|
|
21
|
+
const r = t.createObjectStore("dirty", { keyPath: "entityId" });
|
|
22
|
+
r.indexNames.contains("entityType") || r.createIndex("entityType", "entityType");
|
|
19
23
|
}
|
|
20
|
-
t.objectStoreNames.contains("cursors") || t.createObjectStore("cursors", { keyPath: "groupId" }), t.objectStoreNames.contains("outbox") || t.createObjectStore("outbox", { keyPath: "action.id" }), t.objectStoreNames.contains("sequence") || t.createObjectStore("sequence", { keyPath: "id" }), t.objectStoreNames.contains("replica") || t.createObjectStore("replica", { keyPath: ["type", "id"] }), t.objectStoreNames.contains("replicaCursor") || t.createObjectStore("replicaCursor", { keyPath: "id" }), e < 8 &&
|
|
21
|
-
},
|
|
22
|
-
const e =
|
|
23
|
-
let
|
|
24
|
+
t.objectStoreNames.contains("cursors") || t.createObjectStore("cursors", { keyPath: "groupId" }), t.objectStoreNames.contains("outbox") || t.createObjectStore("outbox", { keyPath: "action.id" }), t.objectStoreNames.contains("sequence") || t.createObjectStore("sequence", { keyPath: "id" }), t.objectStoreNames.contains("replica") || t.createObjectStore("replica", { keyPath: ["type", "id"] }), t.objectStoreNames.contains("replicaCursor") || t.createObjectStore("replicaCursor", { keyPath: "id" }), s ? o.objectStore("sequence").put({ id: w, value: 0 }) : e < 8 && Q(o);
|
|
25
|
+
}, Q = (t) => {
|
|
26
|
+
const e = h(t.objectStore("actions")), a = t.objectStore("sequence");
|
|
27
|
+
let o = 0;
|
|
24
28
|
const s = e.openCursor();
|
|
25
29
|
s.onsuccess = () => {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
a.put({ id:
|
|
30
|
+
const r = s.result;
|
|
31
|
+
if (r === null) {
|
|
32
|
+
a.put({ id: w, value: o });
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
31
|
-
|
|
35
|
+
o += 1, r.update({ ...r.value, position: o }), r.continue();
|
|
32
36
|
};
|
|
33
|
-
},
|
|
37
|
+
}, X = (t) => {
|
|
34
38
|
const e = /* @__PURE__ */ new Set();
|
|
35
39
|
for (const a of t.updates)
|
|
36
40
|
e.add(a.subject_id);
|
|
37
41
|
return Array.from(e);
|
|
38
|
-
},
|
|
39
|
-
const { subject_ids: e, position: a, ...
|
|
40
|
-
return
|
|
41
|
-
},
|
|
42
|
-
const a = t.objectStore("actions"),
|
|
43
|
-
return await a.put({ ...p, position: l, subject_ids:
|
|
44
|
-
},
|
|
42
|
+
}, S = (t) => {
|
|
43
|
+
const { subject_ids: e, position: a, ...o } = t;
|
|
44
|
+
return o;
|
|
45
|
+
}, I = async (t, e) => {
|
|
46
|
+
const a = t.objectStore("actions"), o = await a.get(e.id), s = await t.objectStore("sequence").get(w), r = o === void 0, l = r ? (s?.value ?? 0) + 1 : o.position, p = o === void 0 ? e : { ...e, updates: _(o.updates, e.updates) }, y = T(o?.updates ?? [], e.updates);
|
|
47
|
+
return await a.put({ ...p, position: l, subject_ids: X(p) }), r && await t.objectStore("sequence").put({ id: w, value: l }), { action: p, position: l, added: y };
|
|
48
|
+
}, J = (t) => ({
|
|
45
49
|
async append(e) {
|
|
46
|
-
const a = t.transaction(["actions", "sequence"], "readwrite"),
|
|
47
|
-
return await a.done,
|
|
50
|
+
const a = t.transaction(["actions", "sequence"], "readwrite"), o = await I(a, e);
|
|
51
|
+
return await a.done, o;
|
|
48
52
|
},
|
|
49
53
|
async appendMany(e) {
|
|
50
|
-
const a = t.transaction(["actions", "sequence"], "readwrite"),
|
|
54
|
+
const a = t.transaction(["actions", "sequence"], "readwrite"), o = [];
|
|
51
55
|
for (const s of e)
|
|
52
|
-
|
|
53
|
-
return await a.done,
|
|
56
|
+
o.push(await I(a, s));
|
|
57
|
+
return await a.done, o;
|
|
54
58
|
},
|
|
55
59
|
async readTail(e) {
|
|
56
|
-
const a = t.transaction("actions", "readonly"),
|
|
57
|
-
return await a.done,
|
|
58
|
-
action:
|
|
60
|
+
const a = t.transaction("actions", "readonly"), o = await a.store.index("position").getAll(IDBKeyRange.lowerBound(e, !0));
|
|
61
|
+
return await a.done, o.map((s) => ({
|
|
62
|
+
action: S(s),
|
|
59
63
|
position: s.position,
|
|
60
64
|
added: s.updates
|
|
61
65
|
}));
|
|
62
66
|
},
|
|
63
67
|
async lastPosition() {
|
|
64
|
-
return (await t.get("sequence",
|
|
68
|
+
return (await t.get("sequence", w))?.value ?? 0;
|
|
65
69
|
},
|
|
66
70
|
async getAll() {
|
|
67
|
-
return (await t.getAll("actions")).map(
|
|
71
|
+
return (await t.getAll("actions")).map(S);
|
|
68
72
|
},
|
|
69
73
|
async getForEntity(e) {
|
|
70
|
-
const a = t.transaction("actions", "readonly"),
|
|
71
|
-
return await a.done,
|
|
74
|
+
const a = t.transaction("actions", "readonly"), o = await a.store.index("subject_id").getAll(e);
|
|
75
|
+
return await a.done, o.map(S).sort((r, l) => r.gsn - l.gsn);
|
|
72
76
|
},
|
|
73
77
|
async clear() {
|
|
74
78
|
const e = t.transaction(["actions", "sequence"], "readwrite");
|
|
75
|
-
await e.objectStore("actions").clear(), await e.objectStore("sequence").put({ id:
|
|
79
|
+
await e.objectStore("actions").clear(), await e.objectStore("sequence").put({ id: w, value: 0 }), await e.done;
|
|
76
80
|
}
|
|
77
|
-
}),
|
|
78
|
-
const e = async (a) =>
|
|
81
|
+
}), A = (t) => t === void 0 ? null : v(t), W = (t) => {
|
|
82
|
+
const e = async (a) => A(await t.get("cursors", a));
|
|
79
83
|
return {
|
|
80
84
|
async get(a) {
|
|
81
85
|
return (await t.get("cursors", a))?.cursor ?? null;
|
|
82
86
|
},
|
|
83
|
-
async set(a,
|
|
87
|
+
async set(a, o) {
|
|
84
88
|
const s = await e(a);
|
|
85
|
-
await t.put("cursors", { groupId: a, ...s, cursor:
|
|
89
|
+
await t.put("cursors", { groupId: a, ...s, cursor: o });
|
|
86
90
|
},
|
|
87
91
|
async getRecord(a) {
|
|
88
92
|
return e(a);
|
|
89
93
|
},
|
|
90
|
-
async setRecord(a,
|
|
91
|
-
if (
|
|
94
|
+
async setRecord(a, o) {
|
|
95
|
+
if (O(o)) {
|
|
92
96
|
await t.delete("cursors", a);
|
|
93
97
|
return;
|
|
94
98
|
}
|
|
95
99
|
await t.put("cursors", {
|
|
96
100
|
groupId: a,
|
|
97
|
-
...v(
|
|
101
|
+
...v(o)
|
|
98
102
|
});
|
|
99
103
|
},
|
|
100
104
|
async records() {
|
|
101
|
-
const a = await t.getAll("cursors"),
|
|
105
|
+
const a = await t.getAll("cursors"), o = /* @__PURE__ */ new Map();
|
|
102
106
|
for (const s of a)
|
|
103
|
-
|
|
104
|
-
return
|
|
107
|
+
o.set(s.groupId, A(s) ?? {});
|
|
108
|
+
return o;
|
|
105
109
|
},
|
|
106
110
|
async clear() {
|
|
107
111
|
await t.clear("cursors");
|
|
108
112
|
}
|
|
109
113
|
};
|
|
110
|
-
},
|
|
114
|
+
}, Z = (t) => ({
|
|
111
115
|
async mark(e, a) {
|
|
112
116
|
await t.get("dirty", e) || await t.put("dirty", { entityId: e, entityType: a });
|
|
113
117
|
},
|
|
@@ -115,7 +119,7 @@ const z = 8, m = "actionPosition", A = "replica", Q = (t, e, a, n) => {
|
|
|
115
119
|
return await t.get("dirty", e) !== void 0;
|
|
116
120
|
},
|
|
117
121
|
async getDirtyForType(e) {
|
|
118
|
-
return (await t.getAllFromIndex("dirty", "entityType", e)).map((
|
|
122
|
+
return (await t.getAllFromIndex("dirty", "entityType", e)).map((o) => o.entityId);
|
|
119
123
|
},
|
|
120
124
|
async clear(e) {
|
|
121
125
|
await t.delete("dirty", e);
|
|
@@ -123,85 +127,86 @@ const z = 8, m = "actionPosition", A = "replica", Q = (t, e, a, n) => {
|
|
|
123
127
|
async clearAll() {
|
|
124
128
|
await t.clear("dirty");
|
|
125
129
|
}
|
|
126
|
-
}),
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
const
|
|
131
|
-
await s(
|
|
132
|
-
}, l = async (
|
|
133
|
-
const
|
|
134
|
-
await s(
|
|
135
|
-
}, p = async (
|
|
136
|
-
for (const u of [
|
|
137
|
-
u.remove !== null && await l(u.remove), u.add !== null && await
|
|
138
|
-
}, y = async (
|
|
139
|
-
if (!await a.isDirty(
|
|
140
|
-
const u = await e.getForEntity(
|
|
130
|
+
}), $ = (t, e, a) => {
|
|
131
|
+
const o = (n) => structuredClone(n), s = async (n, i) => {
|
|
132
|
+
i === null ? await t.delete("relationships", n) : await t.put("relationships", { key: n, rows: i });
|
|
133
|
+
}, r = async (n) => {
|
|
134
|
+
const i = await t.get("relationships", n.key);
|
|
135
|
+
await s(n.key, H(i?.rows, n));
|
|
136
|
+
}, l = async (n) => {
|
|
137
|
+
const i = await t.get("relationships", n.key);
|
|
138
|
+
await s(n.key, F(i?.rows, n));
|
|
139
|
+
}, p = async (n, i) => {
|
|
140
|
+
for (const u of [C(n, i), B(n, i)])
|
|
141
|
+
u.remove !== null && await l(u.remove), u.add !== null && await r(u.add);
|
|
142
|
+
}, y = async (n) => {
|
|
143
|
+
if (!await a.isDirty(n)) return;
|
|
144
|
+
const u = await e.getForEntity(n);
|
|
141
145
|
if (u.length === 0) return;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}, d = async (o) => {
|
|
153
|
-
const r = await t.get("relationships", o);
|
|
154
|
-
return B(r?.rows);
|
|
146
|
+
const m = M(u, n);
|
|
147
|
+
if (m === null) return;
|
|
148
|
+
const q = await t.get("entities", n);
|
|
149
|
+
await t.put("entities", o(m)), await p(q, m), await a.clear(n);
|
|
150
|
+
}, c = async (n) => {
|
|
151
|
+
for (const i of await a.getDirtyForType(n))
|
|
152
|
+
await y(i);
|
|
153
|
+
}, d = async (n) => {
|
|
154
|
+
const i = await t.get("relationships", n);
|
|
155
|
+
return P(i?.rows);
|
|
155
156
|
};
|
|
156
157
|
return {
|
|
157
|
-
async get(
|
|
158
|
-
await y(
|
|
159
|
-
const
|
|
160
|
-
return
|
|
158
|
+
async get(n) {
|
|
159
|
+
await y(n);
|
|
160
|
+
const i = await t.get("entities", n);
|
|
161
|
+
return i ? o(i) : null;
|
|
161
162
|
},
|
|
162
|
-
async set(
|
|
163
|
-
const
|
|
164
|
-
await t.put("entities", n
|
|
163
|
+
async set(n) {
|
|
164
|
+
const i = await t.get("entities", n.id);
|
|
165
|
+
await t.put("entities", o(n)), await p(i, n);
|
|
165
166
|
},
|
|
166
|
-
async query(
|
|
167
|
-
return await
|
|
167
|
+
async query(n) {
|
|
168
|
+
return await c(n), (await t.getAllFromIndex("entities", "type", n)).map(o);
|
|
168
169
|
},
|
|
169
170
|
async queryByRelationship({
|
|
170
|
-
as:
|
|
171
|
-
type:
|
|
171
|
+
as: n,
|
|
172
|
+
type: i,
|
|
172
173
|
targetId: u
|
|
173
174
|
}) {
|
|
174
|
-
return await
|
|
175
|
+
return await c(x), d(G(n, i, u));
|
|
175
176
|
},
|
|
176
177
|
async queryByRelationshipSource({
|
|
177
|
-
as:
|
|
178
|
-
type:
|
|
178
|
+
as: n,
|
|
179
|
+
type: i,
|
|
179
180
|
sourceId: u
|
|
180
181
|
}) {
|
|
181
|
-
await
|
|
182
|
-
const
|
|
183
|
-
return
|
|
182
|
+
await c(x);
|
|
183
|
+
const m = await t.get("relationships", k(n, i, u));
|
|
184
|
+
return N(m?.rows);
|
|
184
185
|
},
|
|
185
|
-
async queryByMembership(
|
|
186
|
-
return await
|
|
186
|
+
async queryByMembership(n) {
|
|
187
|
+
return await c(g), d(L(n));
|
|
187
188
|
},
|
|
188
|
-
async queryByMembershipEntity(
|
|
189
|
-
await
|
|
190
|
-
const
|
|
191
|
-
return
|
|
189
|
+
async queryByMembershipEntity(n) {
|
|
190
|
+
await c(g);
|
|
191
|
+
const i = await t.get("relationships", R(n));
|
|
192
|
+
return b(i?.rows);
|
|
192
193
|
},
|
|
193
194
|
async reset() {
|
|
194
195
|
await t.clear("relationships"), await t.clear("entities");
|
|
195
196
|
}
|
|
196
197
|
};
|
|
197
|
-
},
|
|
198
|
+
}, tt = (t) => ({
|
|
199
|
+
...t,
|
|
200
|
+
scope: t.scope ?? "field",
|
|
201
|
+
remoteActions: t.remoteActions ?? []
|
|
202
|
+
}), E = (t) => ({
|
|
198
203
|
action: t.action,
|
|
199
204
|
status: t.status,
|
|
200
205
|
enqueuedAtHlc: t.enqueuedAtHlc,
|
|
201
206
|
// A row predating the field reads as "always concurrent", never
|
|
202
207
|
// silently dropped.
|
|
203
208
|
basedOnGsn: t.basedOnGsn ?? 0,
|
|
204
|
-
conflict: t.conflict
|
|
209
|
+
conflict: t.conflict === void 0 ? void 0 : tt(t.conflict)
|
|
205
210
|
}), et = (t) => ({
|
|
206
211
|
async put(e) {
|
|
207
212
|
await t.put("outbox", {
|
|
@@ -213,17 +218,17 @@ const z = 8, m = "actionPosition", A = "replica", Q = (t, e, a, n) => {
|
|
|
213
218
|
});
|
|
214
219
|
},
|
|
215
220
|
async putAndDelete(e, a) {
|
|
216
|
-
const
|
|
217
|
-
|
|
221
|
+
const o = t.transaction("outbox", "readwrite");
|
|
222
|
+
o.store.put({
|
|
218
223
|
action: e.action,
|
|
219
224
|
status: e.status,
|
|
220
225
|
enqueuedAtHlc: e.enqueuedAtHlc,
|
|
221
226
|
basedOnGsn: e.basedOnGsn,
|
|
222
227
|
conflict: e.conflict
|
|
223
|
-
}),
|
|
228
|
+
}), o.store.delete(a), await o.done;
|
|
224
229
|
},
|
|
225
230
|
async list() {
|
|
226
|
-
return (await t.getAll("outbox")).map(E).sort((a,
|
|
231
|
+
return (await t.getAll("outbox")).map(E).sort((a, o) => U(a.enqueuedAtHlc, o.enqueuedAtHlc));
|
|
227
232
|
},
|
|
228
233
|
async get(e) {
|
|
229
234
|
const a = await t.get("outbox", e);
|
|
@@ -237,26 +242,26 @@ const z = 8, m = "actionPosition", A = "replica", Q = (t, e, a, n) => {
|
|
|
237
242
|
}
|
|
238
243
|
}), at = (t) => ({
|
|
239
244
|
async load() {
|
|
240
|
-
const e = t.transaction(["replica", "replicaCursor"], "readonly"), a = await e.objectStore("replicaCursor").get(
|
|
245
|
+
const e = t.transaction(["replica", "replicaCursor"], "readonly"), a = await e.objectStore("replicaCursor").get(j);
|
|
241
246
|
if (a === void 0)
|
|
242
247
|
return await e.done, null;
|
|
243
|
-
const
|
|
248
|
+
const o = await e.objectStore("replica").getAll();
|
|
244
249
|
await e.done;
|
|
245
250
|
const s = {
|
|
246
251
|
format: a.format,
|
|
247
252
|
position: a.position,
|
|
248
253
|
revision: a.revision,
|
|
249
|
-
entities:
|
|
254
|
+
entities: o
|
|
250
255
|
};
|
|
251
|
-
return
|
|
256
|
+
return K(s) ? s : null;
|
|
252
257
|
},
|
|
253
258
|
async save(e) {
|
|
254
|
-
const a = t.transaction(["replica", "replicaCursor"], "readwrite"),
|
|
255
|
-
await
|
|
259
|
+
const a = t.transaction(["replica", "replicaCursor"], "readwrite"), o = a.objectStore("replica");
|
|
260
|
+
await o.clear();
|
|
256
261
|
for (const s of e.entities)
|
|
257
|
-
await
|
|
262
|
+
await o.put(s);
|
|
258
263
|
await a.objectStore("replicaCursor").put({
|
|
259
|
-
id:
|
|
264
|
+
id: j,
|
|
260
265
|
format: e.format,
|
|
261
266
|
position: e.position,
|
|
262
267
|
revision: e.revision
|
|
@@ -267,53 +272,53 @@ const z = 8, m = "actionPosition", A = "replica", Q = (t, e, a, n) => {
|
|
|
267
272
|
await e.objectStore("replica").clear(), await e.objectStore("replicaCursor").clear(), await e.done;
|
|
268
273
|
}
|
|
269
274
|
}), it = async (t = {}) => {
|
|
270
|
-
const e = t.dbName ?? "ebb-storage", a = await
|
|
271
|
-
upgrade:
|
|
272
|
-
}),
|
|
275
|
+
const e = t.dbName ?? "ebb-storage", a = await D(e, Y, {
|
|
276
|
+
upgrade: z
|
|
277
|
+
}), o = J(a), s = Z(a), r = $(a, o, s), l = W(a), p = et(a), y = at(a);
|
|
273
278
|
return {
|
|
274
279
|
actions: {
|
|
275
|
-
async append(
|
|
276
|
-
const d = await
|
|
277
|
-
for (const f of
|
|
280
|
+
async append(c) {
|
|
281
|
+
const d = await o.append(c);
|
|
282
|
+
for (const f of c.updates)
|
|
278
283
|
await s.mark(f.subject_id, f.subject_type);
|
|
279
284
|
return d;
|
|
280
285
|
},
|
|
281
|
-
async appendMany(
|
|
282
|
-
const d = await
|
|
283
|
-
for (const f of
|
|
284
|
-
for (const
|
|
285
|
-
await s.mark(
|
|
286
|
+
async appendMany(c) {
|
|
287
|
+
const d = await o.appendMany(c);
|
|
288
|
+
for (const f of c)
|
|
289
|
+
for (const n of f.updates)
|
|
290
|
+
await s.mark(n.subject_id, n.subject_type);
|
|
286
291
|
return d;
|
|
287
292
|
},
|
|
288
|
-
async readTail(
|
|
289
|
-
return
|
|
293
|
+
async readTail(c) {
|
|
294
|
+
return o.readTail(c);
|
|
290
295
|
},
|
|
291
296
|
async lastPosition() {
|
|
292
|
-
return
|
|
297
|
+
return o.lastPosition();
|
|
293
298
|
},
|
|
294
299
|
async getAll() {
|
|
295
|
-
return
|
|
300
|
+
return o.getAll();
|
|
296
301
|
},
|
|
297
|
-
async getForEntity(
|
|
298
|
-
return
|
|
302
|
+
async getForEntity(c) {
|
|
303
|
+
return o.getForEntity(c);
|
|
299
304
|
},
|
|
300
305
|
async clear() {
|
|
301
|
-
await
|
|
306
|
+
await o.clear(), await s.clearAll(), await r.reset(), await y.clear();
|
|
302
307
|
}
|
|
303
308
|
},
|
|
304
|
-
entities:
|
|
309
|
+
entities: r,
|
|
305
310
|
dirtyTracker: {
|
|
306
|
-
async mark(
|
|
307
|
-
await s.mark(
|
|
311
|
+
async mark(c, d) {
|
|
312
|
+
await s.mark(c, d);
|
|
308
313
|
},
|
|
309
|
-
async isDirty(
|
|
310
|
-
return s.isDirty(
|
|
314
|
+
async isDirty(c) {
|
|
315
|
+
return s.isDirty(c);
|
|
311
316
|
},
|
|
312
|
-
async getDirtyForType(
|
|
313
|
-
return s.getDirtyForType(
|
|
317
|
+
async getDirtyForType(c) {
|
|
318
|
+
return s.getDirtyForType(c);
|
|
314
319
|
},
|
|
315
|
-
async clear(
|
|
316
|
-
await s.clear(
|
|
320
|
+
async clear(c) {
|
|
321
|
+
await s.clear(c);
|
|
317
322
|
},
|
|
318
323
|
async clearAll() {
|
|
319
324
|
await s.clearAll();
|
|
@@ -322,11 +327,11 @@ const z = 8, m = "actionPosition", A = "replica", Q = (t, e, a, n) => {
|
|
|
322
327
|
cursors: l,
|
|
323
328
|
outbox: p,
|
|
324
329
|
replica: y,
|
|
325
|
-
async isDirty(
|
|
326
|
-
return s.isDirty(
|
|
330
|
+
async isDirty(c) {
|
|
331
|
+
return s.isDirty(c);
|
|
327
332
|
},
|
|
328
333
|
async reset() {
|
|
329
|
-
await
|
|
334
|
+
await o.clear(), await s.clearAll(), await r.reset(), await y.clear(), await l.clear();
|
|
330
335
|
}
|
|
331
336
|
};
|
|
332
337
|
};
|
package/dist/memory/index.d.ts
CHANGED
|
@@ -229,7 +229,8 @@ interface CursorStore {
|
|
|
229
229
|
* `conflicted` is a terminal-until-resolved hold: a catch-up pass
|
|
230
230
|
* classified the Action as a loser, so it is structurally excluded from
|
|
231
231
|
* `pending()` and never submitted. The application resolves it by
|
|
232
|
-
*
|
|
232
|
+
* sending the Action unchanged (back to `pending`) or clearing it
|
|
233
|
+
* (removed).
|
|
233
234
|
*/
|
|
234
235
|
type OutboxStatus = "pending" | "acknowledged" | "error" | "conflicted";
|
|
235
236
|
/**
|
|
@@ -267,6 +268,12 @@ interface ConflictSlot {
|
|
|
267
268
|
/** The value the server holds at the position after the missed window. */
|
|
268
269
|
readonly remote: ConflictSide;
|
|
269
270
|
}
|
|
271
|
+
/** One missed Action that out-dated a buffered write and made it a conflict. */
|
|
272
|
+
interface ConflictRemoteAction {
|
|
273
|
+
readonly actorId: string;
|
|
274
|
+
readonly actionId: string;
|
|
275
|
+
readonly gsn: number;
|
|
276
|
+
}
|
|
270
277
|
/**
|
|
271
278
|
* The before/after entity rows for one losing subject. `before` is the
|
|
272
279
|
* row as the client had it immediately before the catch-up window was
|
|
@@ -305,6 +312,18 @@ interface OutboxConflict {
|
|
|
305
312
|
* application reads that as "nothing of mine would be lost".
|
|
306
313
|
*/
|
|
307
314
|
readonly slots: readonly ConflictSlot[];
|
|
315
|
+
/**
|
|
316
|
+
* The looser of the losing subjects' declared scopes: `"entity"` when
|
|
317
|
+
* any losing subject declared entity scope, else `"field"`. The union is
|
|
318
|
+
* spelled inline so storage stays independent of the schema package.
|
|
319
|
+
*/
|
|
320
|
+
readonly scope: "field" | "component" | "entity";
|
|
321
|
+
/**
|
|
322
|
+
* The concurrent missed Actions that touched the losing subjects,
|
|
323
|
+
* deduped by action id, in window order. The remote half of the
|
|
324
|
+
* report an application builds a policy from.
|
|
325
|
+
*/
|
|
326
|
+
readonly remoteActions: readonly ConflictRemoteAction[];
|
|
308
327
|
/** The catch-up pass that produced this entry. */
|
|
309
328
|
readonly catchUpId: string;
|
|
310
329
|
/**
|
|
@@ -353,10 +372,9 @@ interface OutboxEntry {
|
|
|
353
372
|
interface OutboxStore {
|
|
354
373
|
put(entry: OutboxEntry): Promise<void>;
|
|
355
374
|
/**
|
|
356
|
-
* Persist `entry` and delete `deleteId` in one durable operation
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* without the re-assert persisted.
|
|
375
|
+
* Persist `entry` and delete `deleteId` in one durable operation: the
|
|
376
|
+
* replacement must never exist without the replaced row being gone,
|
|
377
|
+
* nor the row be gone without the replacement persisted.
|
|
360
378
|
*/
|
|
361
379
|
putAndDelete(entry: OutboxEntry, deleteId: string): Promise<void>;
|
|
362
380
|
list(): Promise<readonly OutboxEntry[]>;
|