@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.
@@ -122,9 +122,10 @@ interface MembershipLink {
122
122
  * handled by the DirtyTracker.
123
123
  *
124
124
  * ## Merge Semantics
125
- * - **put** — full entity replacement
126
- * - **patch** — field-level Last-Writer-Wins (LWW): higher HLC wins; tiebreak by lexicographic `update_id`
127
- * - **delete** — soft delete (sets `deleted_hlc`); patch-on-deleted is ignored
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-field map replacement has no single leaf, so
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
- * field an inbound `put` or `delete` reverted also has no triple, and
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 field. */
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 field. */
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 field plus the map-key
254
- * path from the field root to the position. `path` is empty for a plain
255
- * leaf field, an entity-level touch that overlaps no position, or a
256
- * field an inbound `put` / `delete` reverted. Concurrent writes to
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 field: string;
264
- /** Ordered map keys from the field root to the position; empty at the root. */
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
- readonly subjectType: string;
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.subject_type);
492
+ * await storage.dirtyTracker.mark(update.subject_id, updateModel(update, row));
476
493
  * }
477
494
  *
478
495
  * // On entity read:
@@ -1,26 +1,31 @@
1
- import { m as g, a as S, i as E, c as l } from "../cursor-record-D39emd6R.js";
2
- import { l as b, c as M, a as A, f as D, b as _, h as x, k as T, j as h, q as j, M as m, m as v, s as w, r as R, i as k } from "../replica-record-D5gGEoVQ.js";
3
- import { compare as q } from "@ebbjs/core";
4
- const F = (t, n) => {
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.get(s.subject_type);
10
- a ? a.add(s.subject_id) : e.set(s.subject_type, /* @__PURE__ */ new Set([s.subject_id]));
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
- }, C = () => {
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], o = a === void 0 ? e : { ...a, updates: g(a.updates, e.updates) }, c = a === void 0, r = c ? t.actions.length + 1 : s + 1, i = S(a?.updates ?? [], e.updates);
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: c ? [...t.actions, o] : t.actions.map((d) => d.id === e.id ? o : d),
22
- typeIndex: F(t.typeIndex, o)
23
- }, { action: o, position: r, added: i };
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((o) => o.subject_id === e)
48
- ).sort((a, o) => a.gsn - o.gsn);
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
- }, O = () => {
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, o = t.typeIndex[e], c = new Set(o);
80
- c.delete(n), t = {
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]: c
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
- }, u = (t) => JSON.parse(JSON.stringify(t)), f = (t, n, e) => {
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
- }, I = (t, n, e) => x(
100
- x(t, h(n, e)),
101
- T(n, e)
102
- ), P = (t, n) => {
103
- let e = { entities: {}, typeIndex: {}, index: {} };
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 = j(d, r);
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]: u(y) },
113
- typeIndex: f(e.typeIndex, y, p?.type),
114
- index: I(e.index, p, y)
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 i of await n.getDirtyForType(r))
118
- await s(i);
119
- }, o = (r) => _(e.index[r]);
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 i = e.entities[r];
124
- return i ? u(i) : null;
132
+ const c = e.entities[r];
133
+ return c ? l(c) : null;
125
134
  },
126
135
  async set(r) {
127
- const i = e.entities[r.id];
136
+ const c = e.entities[r.id];
128
137
  e = {
129
- entities: { ...e.entities, [r.id]: u(r) },
130
- typeIndex: f(e.typeIndex, r, i?.type),
131
- index: I(e.index, i, r)
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) => u(e.entities[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: i,
154
+ type: c,
140
155
  targetId: d
141
156
  }) {
142
- return await a(w), o(R(r, i, d));
157
+ return await a(x), i(k(r, c, d));
143
158
  },
144
159
  async queryByRelationshipSource({
145
160
  as: r,
146
- type: i,
161
+ type: c,
147
162
  sourceId: d
148
163
  }) {
149
- return await a(w), A(e.index[D(r, i, d)]);
164
+ return await a(x), b(e.index[A(r, c, d)]);
150
165
  },
151
166
  async queryByMembership(r) {
152
- return await a(m), o(v(r));
167
+ return await a(m), i(q(r));
153
168
  },
154
169
  async queryByMembershipEntity(r) {
155
- return await a(m), b(e.index[M(r)]);
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
- }, B = () => {
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]: l({ ...n(e), cursor: s }) }
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 (E(s)) {
178
- const { [e]: a, ...o } = t.records;
179
- t = { records: o };
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]: l(s) } };
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
- }, L = () => {
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) => q(n.enqueuedAtHlc, e.enqueuedAtHlc));
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
- }, N = () => {
231
+ }, V = () => {
217
232
  let t = null;
218
233
  return {
219
234
  async load() {
220
- return k(t) ? t : null;
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
- }, J = () => {
230
- const t = C(), n = O(), e = P(t, n), s = B(), a = L(), o = N();
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(c) {
234
- const r = await t.append(c);
235
- for (const i of c.updates)
236
- await n.mark(i.subject_id, i.subject_type);
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(c) {
240
- const r = await t.appendMany(c);
241
- for (const i of c)
242
- for (const d of i.updates)
243
- await n.mark(d.subject_id, d.subject_type);
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(c) {
247
- return t.readTail(c);
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(c) {
256
- return t.getForEntity(c);
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 o.clear();
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(c, r) {
265
- await n.mark(c, r);
285
+ async mark(o, r) {
286
+ await n.mark(o, r);
266
287
  },
267
- async isDirty(c) {
268
- return n.isDirty(c);
288
+ async isDirty(o) {
289
+ return n.isDirty(o);
269
290
  },
270
- async getDirtyForType(c) {
271
- return n.getDirtyForType(c);
291
+ async getDirtyForType(o) {
292
+ return n.getDirtyForType(o);
272
293
  },
273
- async clear(c) {
274
- await n.clear(c);
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: o,
283
- async isDirty(c) {
284
- return n.isDirty(c);
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 o.clear(), await s.clear();
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
- J as createMemoryAdapter
313
+ X as createMemoryAdapter
293
314
  };
@@ -10,13 +10,23 @@ interface MaterializeOptions {
10
10
  readonly residentMapFields?: boolean;
11
11
  }
12
12
  /**
13
- * Applies a single update to an entity during materialization.
13
+ * Applies a single component Update to a row during materialization.
14
14
  *
15
- * - PUT: full replacement
16
- * - PATCH: field-level LWW merge; ignored on a soft-deleted entity
17
- * - DELETE: soft delete (sets `deleted_hlc`); bumps `last_gsn` and
18
- * `updated_hlc`. A delete for a row that was never materialized is a
19
- * no-op, so a per-Update delivery (#473) cannot break the drain.
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
- iterate(type: string): readonly Entity[];
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
- * `typeIndex` indexes the base; `iterate` layers the overlay's type overrides
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