@ebbjs/storage 1.0.0 → 1.1.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 +32 -15
- package/dist/indexeddb/index.js +180 -162
- package/dist/memory/index.d.ts +33 -16
- package/dist/memory/index.js +104 -83
- package/dist/replica/index.d.ts +39 -10
- package/dist/replica/index.js +162 -155
- package/dist/replica-record-BaptWabD.js +243 -0
- package/dist/types/index.d.ts +51 -17
- package/package.json +2 -2
- package/dist/replica-record-D5gGEoVQ.js +0 -179
package/dist/memory/index.d.ts
CHANGED
|
@@ -122,9 +122,10 @@ interface MembershipLink {
|
|
|
122
122
|
* handled by the DirtyTracker.
|
|
123
123
|
*
|
|
124
124
|
* ## Merge Semantics
|
|
125
|
-
* - **
|
|
126
|
-
* - **
|
|
127
|
-
* - **
|
|
125
|
+
* - **merge** — Last-Writer-Wins (LWW) at the component's field: higher HLC wins; tiebreak by lexicographic `update_id`
|
|
126
|
+
* - **replace** — the component's field is set whole; the first write to an absent row creates it
|
|
127
|
+
* - **drop** — the component's field is removed; the row survives
|
|
128
|
+
* - **delete** — soft delete (sets `deleted_hlc`); the row keeps its type
|
|
128
129
|
*
|
|
129
130
|
* A field value is either a leaf merged by the LWW rule above, or a
|
|
130
131
|
* `{ map }` of nested field values merged key by key under the same
|
|
@@ -136,6 +137,9 @@ interface MembershipLink {
|
|
|
136
137
|
* - `get(id)` — returns entity by ID, materializes if dirty
|
|
137
138
|
* - `set(entity)` — directly sets an entity (bypasses materialization)
|
|
138
139
|
* - `query(type)` — returns all materialized entities of a type
|
|
140
|
+
* - `queryByComponent(component)` — returns the live ids carrying that
|
|
141
|
+
* `<model>/<field>` component in ascending order, materializing dirty rows
|
|
142
|
+
* of the component's model first
|
|
139
143
|
* - `queryByRelationship({ as, type, targetId })` — returns the distinct
|
|
140
144
|
* source ids indexed under that triple in ascending order,
|
|
141
145
|
* materializing dirty relationship rows first
|
|
@@ -154,6 +158,18 @@ interface EntityStore {
|
|
|
154
158
|
get(id: string): Promise<Entity | null>;
|
|
155
159
|
set(entity: Entity): Promise<void>;
|
|
156
160
|
query(type: string): Promise<readonly Entity[]>;
|
|
161
|
+
/**
|
|
162
|
+
* Live rows currently carrying `component` (`<model>/<field>`), ascending by
|
|
163
|
+
* id. Existence is the set of present fields on a row, so this is an index
|
|
164
|
+
* read rather than a row filter; a tombstoned row's components are not
|
|
165
|
+
* existence.
|
|
166
|
+
*
|
|
167
|
+
* The dirty rows materialized first are the component prefix's model. That is
|
|
168
|
+
* the current `<model>/<field>` addressing rule (#797); a component namespace
|
|
169
|
+
* that is not the row's model (#805's `ebb/*` components on ordinary rows)
|
|
170
|
+
* has to decode the model differently and revisit this step.
|
|
171
|
+
*/
|
|
172
|
+
queryByComponent(component: string): Promise<readonly string[]>;
|
|
157
173
|
queryByRelationship(query: RelationshipIndexQuery): Promise<readonly string[]>;
|
|
158
174
|
queryByRelationshipSource(query: RelationshipSourceQuery): Promise<readonly RelationshipLink[]>;
|
|
159
175
|
queryByMembership(groupId: string): Promise<readonly string[]>;
|
|
@@ -236,32 +252,32 @@ type OutboxStatus = "pending" | "acknowledged" | "error" | "conflicted";
|
|
|
236
252
|
/**
|
|
237
253
|
* One side of a contested slot: the buffered local write, or the value
|
|
238
254
|
* the server holds once the catch-up window is applied. A leaf carries
|
|
239
|
-
* its LWW triple; a whole-
|
|
255
|
+
* its LWW triple; a whole-component map replacement has no single leaf, so
|
|
240
256
|
* `update_id` / `hlc` are absent and `value` is the projected map. A
|
|
241
|
-
*
|
|
257
|
+
* component an inbound `merge` or `delete` reverted also has no triple, and
|
|
242
258
|
* `value` is `undefined` because the server holds no value.
|
|
243
259
|
*/
|
|
244
260
|
interface ConflictSide {
|
|
245
261
|
/** Projected leaf or map value. `undefined` when the server holds nothing. */
|
|
246
262
|
readonly value: unknown;
|
|
247
|
-
/** `update_id` of the FieldValue; absent for a map side or a reverted
|
|
263
|
+
/** `update_id` of the FieldValue; absent for a map side or a reverted component. */
|
|
248
264
|
readonly update_id?: string;
|
|
249
|
-
/** HLC carried by the FieldValue; absent for a map side or a reverted
|
|
265
|
+
/** HLC carried by the FieldValue; absent for a map side or a reverted component. */
|
|
250
266
|
readonly hlc?: string;
|
|
251
267
|
}
|
|
252
268
|
/**
|
|
253
|
-
* One contested position on a losing Action: a
|
|
254
|
-
* path from the
|
|
255
|
-
* leaf
|
|
256
|
-
*
|
|
269
|
+
* One contested position on a losing Action: a component plus the map-key
|
|
270
|
+
* path from the component's value root to the position. `path` is empty for a
|
|
271
|
+
* plain leaf component, an entity-level touch that overlaps no position, or a
|
|
272
|
+
* component an inbound `merge` / `delete` reverted. Concurrent writes to
|
|
257
273
|
* different keys are different slots and so are not conflicts.
|
|
258
274
|
*/
|
|
259
275
|
interface ConflictSlot {
|
|
260
276
|
/** The `subject_id` of the Update that carried the write. */
|
|
261
277
|
readonly subjectId: string;
|
|
262
|
-
/** The field name on that subject. */
|
|
263
|
-
readonly
|
|
264
|
-
/** Ordered map keys from the
|
|
278
|
+
/** The `<model>/<field>` component name on that subject. */
|
|
279
|
+
readonly component: string;
|
|
280
|
+
/** Ordered map keys from the component root to the position; empty at the root. */
|
|
265
281
|
readonly path: readonly string[];
|
|
266
282
|
/** The buffered local value at the position. */
|
|
267
283
|
readonly local: ConflictSide;
|
|
@@ -285,7 +301,8 @@ interface ConflictRemoteAction {
|
|
|
285
301
|
*/
|
|
286
302
|
interface ConflictSubjectState {
|
|
287
303
|
readonly subjectId: string;
|
|
288
|
-
|
|
304
|
+
/** The model the row is committed under. */
|
|
305
|
+
readonly model: string;
|
|
289
306
|
readonly before: Entity | null;
|
|
290
307
|
readonly after: Entity | null;
|
|
291
308
|
}
|
|
@@ -472,7 +489,7 @@ interface StorageAdapter {
|
|
|
472
489
|
* // On action receipt:
|
|
473
490
|
* await storage.actions.append(action);
|
|
474
491
|
* for (const update of action.updates) {
|
|
475
|
-
* await storage.dirtyTracker.mark(update.subject_id, update
|
|
492
|
+
* await storage.dirtyTracker.mark(update.subject_id, updateModel(update, row));
|
|
476
493
|
* }
|
|
477
494
|
*
|
|
478
495
|
* // On entity read:
|
package/dist/memory/index.js
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
1
|
+
import { l as E, c as D, a as b, f as A, d as _, b as T, n as u, p as h, o as v, q as R, s as j, z as C, M as m, m as q, A as x, r as k, i as F, u as w } from "../replica-record-BaptWabD.js";
|
|
2
|
+
import { componentModel as M, compare as O } from "@ebbjs/core";
|
|
3
|
+
import { m as B, a as P, i as L, c as f } from "../cursor-record-D39emd6R.js";
|
|
4
|
+
const N = (t, n) => {
|
|
5
|
+
for (const [e, s] of t)
|
|
6
|
+
if (s.has(n)) return e;
|
|
7
|
+
}, H = (t, n) => {
|
|
5
8
|
const e = /* @__PURE__ */ new Map();
|
|
6
9
|
for (const [s, a] of t)
|
|
7
10
|
e.set(s, new Set(a));
|
|
8
11
|
for (const s of n.updates) {
|
|
9
|
-
const a = e.
|
|
10
|
-
|
|
12
|
+
const a = s.method === "delete" ? N(e, s.subject_id) : M(s.component);
|
|
13
|
+
if (a === void 0) continue;
|
|
14
|
+
const i = e.get(a);
|
|
15
|
+
i ? i.add(s.subject_id) : e.set(a, /* @__PURE__ */ new Set([s.subject_id]));
|
|
11
16
|
}
|
|
12
17
|
return Array.from(e.entries()).map(([s, a]) => [s, Array.from(a)]);
|
|
13
|
-
},
|
|
18
|
+
}, K = () => {
|
|
14
19
|
let t = {
|
|
15
20
|
actions: [],
|
|
16
21
|
typeIndex: []
|
|
17
22
|
};
|
|
18
23
|
const n = async (e) => {
|
|
19
|
-
const s = t.actions.findIndex((d) => d.id === e.id), a = s === -1 ? void 0 : t.actions[s],
|
|
24
|
+
const s = t.actions.findIndex((d) => d.id === e.id), a = s === -1 ? void 0 : t.actions[s], i = a === void 0 ? e : { ...a, updates: B(a.updates, e.updates) }, o = a === void 0, r = o ? t.actions.length + 1 : s + 1, c = P(a?.updates ?? [], e.updates);
|
|
20
25
|
return t = {
|
|
21
|
-
actions:
|
|
22
|
-
typeIndex:
|
|
23
|
-
}, { action:
|
|
26
|
+
actions: o ? [...t.actions, i] : t.actions.map((d) => d.id === e.id ? i : d),
|
|
27
|
+
typeIndex: H(t.typeIndex, i)
|
|
28
|
+
}, { action: i, position: r, added: c };
|
|
24
29
|
};
|
|
25
30
|
return {
|
|
26
31
|
append: n,
|
|
@@ -44,14 +49,14 @@ const F = (t, n) => {
|
|
|
44
49
|
},
|
|
45
50
|
async getForEntity(e) {
|
|
46
51
|
return t.actions.filter(
|
|
47
|
-
(a) => a.updates.some((
|
|
48
|
-
).sort((a,
|
|
52
|
+
(a) => a.updates.some((i) => i.subject_id === e)
|
|
53
|
+
).sort((a, i) => a.gsn - i.gsn);
|
|
49
54
|
},
|
|
50
55
|
async clear() {
|
|
51
56
|
t = { actions: [], typeIndex: [] };
|
|
52
57
|
}
|
|
53
58
|
};
|
|
54
|
-
},
|
|
59
|
+
}, Y = () => {
|
|
55
60
|
let t = {
|
|
56
61
|
entities: {},
|
|
57
62
|
typeIndex: {}
|
|
@@ -76,12 +81,12 @@ const F = (t, n) => {
|
|
|
76
81
|
async clear(n) {
|
|
77
82
|
const e = t.entities[n];
|
|
78
83
|
if (!e) return;
|
|
79
|
-
const { [n]: s, ...a } = t.entities,
|
|
80
|
-
|
|
84
|
+
const { [n]: s, ...a } = t.entities, i = t.typeIndex[e], o = new Set(i);
|
|
85
|
+
o.delete(n), t = {
|
|
81
86
|
entities: a,
|
|
82
87
|
typeIndex: {
|
|
83
88
|
...t.typeIndex,
|
|
84
|
-
[e]:
|
|
89
|
+
[e]: o
|
|
85
90
|
}
|
|
86
91
|
};
|
|
87
92
|
},
|
|
@@ -89,76 +94,86 @@ const F = (t, n) => {
|
|
|
89
94
|
t = { entities: {}, typeIndex: {} };
|
|
90
95
|
}
|
|
91
96
|
};
|
|
92
|
-
},
|
|
97
|
+
}, l = (t) => JSON.parse(JSON.stringify(t)), I = (t, n, e) => {
|
|
93
98
|
const s = { ...t };
|
|
94
99
|
if (e && e !== n.type) {
|
|
95
100
|
const a = new Set(s[e] ?? []);
|
|
96
101
|
a.delete(n.id), s[e] = a;
|
|
97
102
|
}
|
|
98
103
|
return s[n.type] || (s[n.type] = /* @__PURE__ */ new Set()), s[n.type] = new Set(s[n.type]).add(n.id), s;
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
}, g = (t, n, e) => {
|
|
105
|
+
const s = R(n, e);
|
|
106
|
+
return s === null ? t : j(t, s);
|
|
107
|
+
}, S = (t, n, e) => u(
|
|
108
|
+
u(t, v(n, e)),
|
|
109
|
+
h(n, e)
|
|
110
|
+
), z = (t, n) => {
|
|
111
|
+
let e = { entities: {}, typeIndex: {}, components: /* @__PURE__ */ new Map(), index: {} };
|
|
104
112
|
const s = async (r) => {
|
|
105
113
|
if (!await n.isDirty(r)) return;
|
|
106
114
|
const d = await t.getForEntity(r);
|
|
107
115
|
if (d.length === 0) return;
|
|
108
|
-
const y =
|
|
116
|
+
const y = C(d, r);
|
|
109
117
|
if (y === null) return;
|
|
110
118
|
const p = e.entities[r];
|
|
111
119
|
e = {
|
|
112
|
-
entities: { ...e.entities, [r]:
|
|
113
|
-
typeIndex:
|
|
114
|
-
|
|
120
|
+
entities: { ...e.entities, [r]: l(y) },
|
|
121
|
+
typeIndex: I(e.typeIndex, y, p?.type),
|
|
122
|
+
components: g(e.components, p, y),
|
|
123
|
+
index: S(e.index, p, y)
|
|
115
124
|
}, await n.clear(r);
|
|
116
125
|
}, a = async (r) => {
|
|
117
|
-
for (const
|
|
118
|
-
await s(
|
|
119
|
-
},
|
|
126
|
+
for (const c of await n.getDirtyForType(r))
|
|
127
|
+
await s(c);
|
|
128
|
+
}, i = (r) => T(e.index[r]);
|
|
120
129
|
return {
|
|
121
130
|
async get(r) {
|
|
122
131
|
await s(r);
|
|
123
|
-
const
|
|
124
|
-
return
|
|
132
|
+
const c = e.entities[r];
|
|
133
|
+
return c ? l(c) : null;
|
|
125
134
|
},
|
|
126
135
|
async set(r) {
|
|
127
|
-
const
|
|
136
|
+
const c = e.entities[r.id];
|
|
128
137
|
e = {
|
|
129
|
-
entities: { ...e.entities, [r.id]:
|
|
130
|
-
typeIndex:
|
|
131
|
-
|
|
138
|
+
entities: { ...e.entities, [r.id]: l(r) },
|
|
139
|
+
typeIndex: I(e.typeIndex, r, c?.type),
|
|
140
|
+
components: g(e.components, c, r),
|
|
141
|
+
index: S(e.index, c, r)
|
|
132
142
|
};
|
|
133
143
|
},
|
|
134
144
|
async query(r) {
|
|
135
|
-
return await a(r), [...e.typeIndex[r] ?? /* @__PURE__ */ new Set()].map((d) =>
|
|
145
|
+
return await a(r), [...e.typeIndex[r] ?? /* @__PURE__ */ new Set()].map((d) => l(e.entities[d]));
|
|
146
|
+
},
|
|
147
|
+
async queryByComponent(r) {
|
|
148
|
+
return await a(M(r)), _(e.components, r).filter(
|
|
149
|
+
(c) => e.entities[c]?.deleted_hlc === null
|
|
150
|
+
);
|
|
136
151
|
},
|
|
137
152
|
async queryByRelationship({
|
|
138
153
|
as: r,
|
|
139
|
-
type:
|
|
154
|
+
type: c,
|
|
140
155
|
targetId: d
|
|
141
156
|
}) {
|
|
142
|
-
return await a(
|
|
157
|
+
return await a(x), i(k(r, c, d));
|
|
143
158
|
},
|
|
144
159
|
async queryByRelationshipSource({
|
|
145
160
|
as: r,
|
|
146
|
-
type:
|
|
161
|
+
type: c,
|
|
147
162
|
sourceId: d
|
|
148
163
|
}) {
|
|
149
|
-
return await a(
|
|
164
|
+
return await a(x), b(e.index[A(r, c, d)]);
|
|
150
165
|
},
|
|
151
166
|
async queryByMembership(r) {
|
|
152
|
-
return await a(m),
|
|
167
|
+
return await a(m), i(q(r));
|
|
153
168
|
},
|
|
154
169
|
async queryByMembershipEntity(r) {
|
|
155
|
-
return await a(m),
|
|
170
|
+
return await a(m), E(e.index[D(r)]);
|
|
156
171
|
},
|
|
157
172
|
async reset() {
|
|
158
|
-
e = { entities: {}, typeIndex: {}, index: {} };
|
|
173
|
+
e = { entities: {}, typeIndex: {}, components: /* @__PURE__ */ new Map(), index: {} };
|
|
159
174
|
}
|
|
160
175
|
};
|
|
161
|
-
},
|
|
176
|
+
}, J = () => {
|
|
162
177
|
let t = { records: {} };
|
|
163
178
|
const n = (e) => t.records[e] ?? null;
|
|
164
179
|
return {
|
|
@@ -167,19 +182,19 @@ const F = (t, n) => {
|
|
|
167
182
|
},
|
|
168
183
|
async set(e, s) {
|
|
169
184
|
t = {
|
|
170
|
-
records: { ...t.records, [e]:
|
|
185
|
+
records: { ...t.records, [e]: f({ ...n(e), cursor: s }) }
|
|
171
186
|
};
|
|
172
187
|
},
|
|
173
188
|
async getRecord(e) {
|
|
174
189
|
return n(e);
|
|
175
190
|
},
|
|
176
191
|
async setRecord(e, s) {
|
|
177
|
-
if (
|
|
178
|
-
const { [e]: a, ...
|
|
179
|
-
t = { records:
|
|
192
|
+
if (L(s)) {
|
|
193
|
+
const { [e]: a, ...i } = t.records;
|
|
194
|
+
t = { records: i };
|
|
180
195
|
return;
|
|
181
196
|
}
|
|
182
|
-
t = { records: { ...t.records, [e]:
|
|
197
|
+
t = { records: { ...t.records, [e]: f(s) } };
|
|
183
198
|
},
|
|
184
199
|
async records() {
|
|
185
200
|
return new Map(Object.entries(t.records));
|
|
@@ -188,7 +203,7 @@ const F = (t, n) => {
|
|
|
188
203
|
t = { records: {} };
|
|
189
204
|
}
|
|
190
205
|
};
|
|
191
|
-
},
|
|
206
|
+
}, U = () => {
|
|
192
207
|
let t = {};
|
|
193
208
|
return {
|
|
194
209
|
async put(n) {
|
|
@@ -199,7 +214,7 @@ const F = (t, n) => {
|
|
|
199
214
|
t = { ...a, [n.action.id]: structuredClone(n) };
|
|
200
215
|
},
|
|
201
216
|
async list() {
|
|
202
|
-
return Object.values(t).map((n) => structuredClone(n)).sort((n, e) =>
|
|
217
|
+
return Object.values(t).map((n) => structuredClone(n)).sort((n, e) => O(n.enqueuedAtHlc, e.enqueuedAtHlc));
|
|
203
218
|
},
|
|
204
219
|
async get(n) {
|
|
205
220
|
const e = t[n];
|
|
@@ -213,11 +228,11 @@ const F = (t, n) => {
|
|
|
213
228
|
t = {};
|
|
214
229
|
}
|
|
215
230
|
};
|
|
216
|
-
},
|
|
231
|
+
}, V = () => {
|
|
217
232
|
let t = null;
|
|
218
233
|
return {
|
|
219
234
|
async load() {
|
|
220
|
-
return
|
|
235
|
+
return F(t) ? t : null;
|
|
221
236
|
},
|
|
222
237
|
async save(n) {
|
|
223
238
|
t = n;
|
|
@@ -226,25 +241,31 @@ const F = (t, n) => {
|
|
|
226
241
|
t = null;
|
|
227
242
|
}
|
|
228
243
|
};
|
|
229
|
-
},
|
|
230
|
-
const t =
|
|
244
|
+
}, X = () => {
|
|
245
|
+
const t = K(), n = Y(), e = z(t, n), s = J(), a = U(), i = V();
|
|
231
246
|
return {
|
|
232
247
|
actions: {
|
|
233
|
-
async append(
|
|
234
|
-
const r = await t.append(
|
|
235
|
-
for (const
|
|
236
|
-
await n.mark(
|
|
248
|
+
async append(o) {
|
|
249
|
+
const r = await t.append(o);
|
|
250
|
+
for (const c of o.updates)
|
|
251
|
+
await n.mark(
|
|
252
|
+
c.subject_id,
|
|
253
|
+
w(c, await e.get(c.subject_id))
|
|
254
|
+
);
|
|
237
255
|
return r;
|
|
238
256
|
},
|
|
239
|
-
async appendMany(
|
|
240
|
-
const r = await t.appendMany(
|
|
241
|
-
for (const
|
|
242
|
-
for (const d of
|
|
243
|
-
await n.mark(
|
|
257
|
+
async appendMany(o) {
|
|
258
|
+
const r = await t.appendMany(o);
|
|
259
|
+
for (const c of o)
|
|
260
|
+
for (const d of c.updates)
|
|
261
|
+
await n.mark(
|
|
262
|
+
d.subject_id,
|
|
263
|
+
w(d, await e.get(d.subject_id))
|
|
264
|
+
);
|
|
244
265
|
return r;
|
|
245
266
|
},
|
|
246
|
-
async readTail(
|
|
247
|
-
return t.readTail(
|
|
267
|
+
async readTail(o) {
|
|
268
|
+
return t.readTail(o);
|
|
248
269
|
},
|
|
249
270
|
async lastPosition() {
|
|
250
271
|
return t.lastPosition();
|
|
@@ -252,26 +273,26 @@ const F = (t, n) => {
|
|
|
252
273
|
async getAll() {
|
|
253
274
|
return t.getAll();
|
|
254
275
|
},
|
|
255
|
-
async getForEntity(
|
|
256
|
-
return t.getForEntity(
|
|
276
|
+
async getForEntity(o) {
|
|
277
|
+
return t.getForEntity(o);
|
|
257
278
|
},
|
|
258
279
|
async clear() {
|
|
259
|
-
await t.clear(), await n.clearAll(), await e.reset(), await
|
|
280
|
+
await t.clear(), await n.clearAll(), await e.reset(), await i.clear();
|
|
260
281
|
}
|
|
261
282
|
},
|
|
262
283
|
entities: e,
|
|
263
284
|
dirtyTracker: {
|
|
264
|
-
async mark(
|
|
265
|
-
await n.mark(
|
|
285
|
+
async mark(o, r) {
|
|
286
|
+
await n.mark(o, r);
|
|
266
287
|
},
|
|
267
|
-
async isDirty(
|
|
268
|
-
return n.isDirty(
|
|
288
|
+
async isDirty(o) {
|
|
289
|
+
return n.isDirty(o);
|
|
269
290
|
},
|
|
270
|
-
async getDirtyForType(
|
|
271
|
-
return n.getDirtyForType(
|
|
291
|
+
async getDirtyForType(o) {
|
|
292
|
+
return n.getDirtyForType(o);
|
|
272
293
|
},
|
|
273
|
-
async clear(
|
|
274
|
-
await n.clear(
|
|
294
|
+
async clear(o) {
|
|
295
|
+
await n.clear(o);
|
|
275
296
|
},
|
|
276
297
|
async clearAll() {
|
|
277
298
|
await n.clearAll();
|
|
@@ -279,15 +300,15 @@ const F = (t, n) => {
|
|
|
279
300
|
},
|
|
280
301
|
cursors: s,
|
|
281
302
|
outbox: a,
|
|
282
|
-
replica:
|
|
283
|
-
async isDirty(
|
|
284
|
-
return n.isDirty(
|
|
303
|
+
replica: i,
|
|
304
|
+
async isDirty(o) {
|
|
305
|
+
return n.isDirty(o);
|
|
285
306
|
},
|
|
286
307
|
async reset() {
|
|
287
|
-
await t.clear(), await n.clearAll(), await e.reset(), await
|
|
308
|
+
await t.clear(), await n.clearAll(), await e.reset(), await i.clear(), await s.clear();
|
|
288
309
|
}
|
|
289
310
|
};
|
|
290
311
|
};
|
|
291
312
|
export {
|
|
292
|
-
|
|
313
|
+
X as createMemoryAdapter
|
|
293
314
|
};
|
package/dist/replica/index.d.ts
CHANGED
|
@@ -10,13 +10,23 @@ interface MaterializeOptions {
|
|
|
10
10
|
readonly residentMapFields?: boolean;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Applies a single
|
|
13
|
+
* Applies a single component Update to a row during materialization.
|
|
14
14
|
*
|
|
15
|
-
* -
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
15
|
+
* - `merge` / `replace`: apply the component's value at its field under the
|
|
16
|
+
* leaf-level LWW compare. The first such write on a row that does not exist
|
|
17
|
+
* creates it; `replace` is the row's create, while `merge` leaves
|
|
18
|
+
* `exists: false` and `created_hlc: null` because the create has not been
|
|
19
|
+
* seen, so the row is a backfill candidate.
|
|
20
|
+
* - `drop`: remove the field from an existing row; the row survives with its
|
|
21
|
+
* existence and creation time, even when the last component is detached.
|
|
22
|
+
* A tombstone stays a tombstone — detaching a component is not a
|
|
23
|
+
* resurrection.
|
|
24
|
+
* - `delete`: tombstone the row, keeping its type and existence bit. A delete
|
|
25
|
+
* for a row that was never materialized is a no-op, so a per-Update delivery
|
|
26
|
+
* (#473) cannot break the drain.
|
|
27
|
+
*
|
|
28
|
+
* A component write to a tombstoned row resurrects it, exactly as the server's
|
|
29
|
+
* materialization does.
|
|
20
30
|
*/
|
|
21
31
|
declare const applyUpdate: (entity: Entity | null, update: Update, gsn: number, hlc: HLCTimestamp, options?: MaterializeOptions) => Entity | null;
|
|
22
32
|
/**
|
|
@@ -89,8 +99,15 @@ type Revision = number;
|
|
|
89
99
|
interface ReplicaChange {
|
|
90
100
|
readonly revision: Revision;
|
|
91
101
|
readonly id: string;
|
|
92
|
-
readonly type: string;
|
|
93
102
|
readonly deleted: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* The components this change touched, so a derived reader invalidates on what
|
|
105
|
+
* it read rather than on the row's whole model. A tombstone transition touches
|
|
106
|
+
* every component the row carries, and a write that moves a row between models
|
|
107
|
+
* names each field under both prefixes. Empty only for a row that carried no
|
|
108
|
+
* component.
|
|
109
|
+
*/
|
|
110
|
+
readonly components: readonly string[];
|
|
94
111
|
}
|
|
95
112
|
/**
|
|
96
113
|
* Whether the replica retains a type's rows. The seam #500 builds on-demand
|
|
@@ -190,7 +207,11 @@ interface Replica {
|
|
|
190
207
|
* with gsn 0, which would hide the row's folded position.
|
|
191
208
|
*/
|
|
192
209
|
base(id: string): Entity | undefined;
|
|
193
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Every row of `model`, whatever optional components it carries. Served by
|
|
212
|
+
* the model index with the overlay's model overrides layered on top.
|
|
213
|
+
*/
|
|
214
|
+
iterate(model: string): readonly Entity[];
|
|
194
215
|
/** Whether the materialization policy holds `type` in full. */
|
|
195
216
|
isHeld(type: string): boolean;
|
|
196
217
|
/**
|
|
@@ -221,6 +242,12 @@ interface Replica {
|
|
|
221
242
|
* live sources pointing at `(as, type, targetId)`, ascending.
|
|
222
243
|
*/
|
|
223
244
|
relationships(query: RelationshipQuery): readonly string[];
|
|
245
|
+
/**
|
|
246
|
+
* Ids currently carrying `component` (`<type>/<field>`) in the composed
|
|
247
|
+
* view, ascending. Component existence is the set of present fields on a
|
|
248
|
+
* row, so a query reading it costs its index rather than a row filter.
|
|
249
|
+
*/
|
|
250
|
+
components(component: string): readonly string[];
|
|
224
251
|
/**
|
|
225
252
|
* Distinct live member entity ids of a group, or distinct live group
|
|
226
253
|
* ids of an entity, ascending.
|
|
@@ -271,8 +298,10 @@ declare const DEFAULT_PINNED_TTL_MS = 30000;
|
|
|
271
298
|
* persists, once pinned on-demand rows are filtered out), `overlay` composes
|
|
272
299
|
* optimistic writes over it, and `effective` caches the composed view for
|
|
273
300
|
* overlaid ids so a read never recomposes.
|
|
274
|
-
* `
|
|
275
|
-
* on top of it.
|
|
301
|
+
* `modelIndex` indexes the base by model; `iterate` layers the overlay's model
|
|
302
|
+
* overrides on top of it. `componentIndex` tracks the composed view's component
|
|
303
|
+
* set alongside the adjacency indices, and is likewise derived rather than
|
|
304
|
+
* persisted.
|
|
276
305
|
*/
|
|
277
306
|
declare const createReplica: (options?: ReplicaOptions) => Replica;
|
|
278
307
|
|