@sovereignbase/convergent-replicated-list 1.0.0 → 1.0.2

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/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * The `predecessor` field is the stable ordering anchor used for convergence;
5
5
  * `prev` and `next` are local projection links for indexed reads and mutations.
6
6
  */
7
- type DoublyLinkedListEntry<T> = {
7
+ type CRListStateEntry<T> = {
8
8
  /** Stable UUIDv7 identity for this entry. */
9
9
  uuidv7: string;
10
10
  /** User payload stored in the list. */
@@ -14,9 +14,9 @@ type DoublyLinkedListEntry<T> = {
14
14
  /** Current zero-based index in the local live view. */
15
15
  index: number;
16
16
  /** Previous live entry in the local projection. */
17
- prev: DoublyLinkedListEntry<T> | undefined;
17
+ prev: CRListStateEntry<T> | undefined;
18
18
  /** Next live entry in the local projection. */
19
- next: DoublyLinkedListEntry<T> | undefined;
19
+ next: CRListStateEntry<T> | undefined;
20
20
  } | undefined;
21
21
  /**
22
22
  * Mutable CRList replica state.
@@ -26,22 +26,22 @@ type DoublyLinkedListEntry<T> = {
26
26
  * UUIDv7 entries until they are garbage collected through acknowledgement
27
27
  * frontiers.
28
28
  */
29
- type CRListReplica<T> = {
29
+ type CRListState<T> = {
30
30
  /** Number of live entries in the local projection. */
31
31
  size: number;
32
32
  /** Current live entry used as the walking cursor. */
33
- cursor: DoublyLinkedListEntry<T>;
33
+ cursor: CRListStateEntry<T>;
34
34
  /** Deleted UUIDv7 entries retained for gossip and convergence. */
35
35
  tombstones: Set<string>;
36
36
  /** Live entries by UUIDv7. */
37
- parentMap: Map<string, DoublyLinkedListEntry<T>>;
37
+ parentMap: Map<string, CRListStateEntry<T>>;
38
38
  /** Live entries grouped by stable predecessor identifier. */
39
- childrenMap: Map<string, Array<NonNullable<DoublyLinkedListEntry<T>>>>;
39
+ childrenMap: Map<string, Array<NonNullable<CRListStateEntry<T>>>>;
40
40
  };
41
41
  /**
42
42
  * Serializable value entry used by snapshots and deltas.
43
43
  */
44
- type CRListSnapshotValueEntry<T> = {
44
+ type CRListSnapshotEntry<T> = {
45
45
  /** Stable UUIDv7 identity for this entry. */
46
46
  uuidv7: string;
47
47
  /** User payload for this entry. */
@@ -54,14 +54,10 @@ type CRListSnapshotValueEntry<T> = {
54
54
  */
55
55
  type CRListSnapshot<T> = {
56
56
  /** Serializable live values. */
57
- values: Array<CRListSnapshotValueEntry<T>>;
57
+ values: Array<CRListSnapshotEntry<T>>;
58
58
  /** Retained deleted UUIDv7 entries. */
59
59
  tombstones: Array<string>;
60
60
  };
61
- /**
62
- * Partial CRList state gossiped between replicas.
63
- */
64
- type CRListDelta<T> = Partial<CRListSnapshot<T>>;
65
61
  /**
66
62
  * Minimal local live-view patch keyed by list index.
67
63
  *
@@ -70,8 +66,9 @@ type CRListDelta<T> = Partial<CRListSnapshot<T>>;
70
66
  */
71
67
  type CRListChange<T> = Record<number, T | undefined>;
72
68
  /**
73
- * Tombstone acknowledgement frontier.
69
+ * Partial CRList state gossiped between replicas.
74
70
  */
71
+ type CRListDelta<T> = Partial<CRListSnapshot<T>>;
75
72
  type CRListAck = string;
76
73
  /**
77
74
  * Maps CRList event names to their event payload shapes.
@@ -95,6 +92,130 @@ type CRListEventListener<T, K extends keyof CRListEventMap<T>> = ((event: Custom
95
92
  */
96
93
  type CRListEventListenerFor<T, K extends string> = K extends keyof CRListEventMap<T> ? CRListEventListener<T, K> : EventListenerOrEventListenerObject;
97
94
 
95
+ /**
96
+ * A convergent replicated list.
97
+ *
98
+ * Numeric property access reads and mutates the live list projection:
99
+ * `list[0]` reads a detached copy of an entry, `list[0] = value` writes an
100
+ * entry, and `delete list[0]` removes one entry. Iteration and `forEach()`
101
+ * likewise expose detached copies rather than mutable references into the
102
+ * replica state. Local mutations emit `delta` and `change` events; remote
103
+ * merges emit `change` events.
104
+ *
105
+ * @typeParam T - The value type stored in the list.
106
+ */
107
+ declare class CRList<T> {
108
+ /**
109
+ * Reads or overwrites an entry in the live list projection by index.
110
+ *
111
+ * Reads return detached copies.
112
+ */
113
+ [index: number]: T;
114
+ private readonly state;
115
+ private readonly eventTarget;
116
+ /**
117
+ * Creates a replicated list from an optional serializable snapshot.
118
+ *
119
+ * @param snapshot - A previously emitted CRList snapshot.
120
+ */
121
+ constructor(snapshot?: CRListSnapshot<T>);
122
+ /**
123
+ * The current number of live entries.
124
+ */
125
+ get size(): number;
126
+ /**
127
+ * Inserts a value before an index.
128
+ *
129
+ * If `beforeIndex` is omitted, the value is inserted at the start of the list.
130
+ *
131
+ * @param value - The value to insert.
132
+ * @param beforeIndex - The index to insert before.
133
+ */
134
+ prepend(value: T, beforeIndex?: number): void;
135
+ /**
136
+ * Inserts a value after an index.
137
+ *
138
+ * If `afterIndex` is omitted, the value is appended at the end of the list.
139
+ *
140
+ * @param value - The value to insert.
141
+ * @param afterIndex - The index to insert after.
142
+ */
143
+ append(value: T, afterIndex?: number): void;
144
+ /**
145
+ * Removes the entry at an index.
146
+ *
147
+ * @param index - The index to remove.
148
+ */
149
+ remove(index: number): void;
150
+ /**
151
+ * Applies a remote gossip delta to this list.
152
+ *
153
+ * Emits a `change` event when the merge changes the live projection.
154
+ *
155
+ * @param delta - The remote CRList delta to merge.
156
+ */
157
+ merge(delta: CRListDelta<T>): void;
158
+ /**
159
+ * Emits an acknowledgement frontier for currently retained tombstones.
160
+ */
161
+ acknowledge(): void;
162
+ /**
163
+ * Garbage-collects tombstones that are covered by acknowledgement frontiers.
164
+ *
165
+ * @param frontiers - Replica acknowledgement frontiers.
166
+ */
167
+ garbageCollect(frontiers: Array<CRListAck>): void;
168
+ /**
169
+ * Emits the current serializable list snapshot.
170
+ */
171
+ snapshot(): void;
172
+ /**
173
+ * Registers an event listener.
174
+ *
175
+ * @param type - The event type to listen for.
176
+ * @param listener - The listener to register.
177
+ * @param options - Listener registration options.
178
+ */
179
+ addEventListener<K extends keyof CRListEventMap<T>>(type: K, listener: CRListEventListenerFor<T, K> | null, options?: boolean | AddEventListenerOptions): void;
180
+ /**
181
+ * Removes an event listener.
182
+ *
183
+ * @param type - The event type to stop listening for.
184
+ * @param listener - The listener to remove.
185
+ * @param options - Listener removal options.
186
+ */
187
+ removeEventListener<K extends keyof CRListEventMap<T>>(type: K, listener: CRListEventListenerFor<T, K> | null, options?: boolean | EventListenerOptions): void;
188
+ /**
189
+ * Returns a serializable snapshot representation of this list.
190
+ *
191
+ * Called automatically by `JSON.stringify`.
192
+ */
193
+ toJSON(): CRListSnapshot<T>;
194
+ /**
195
+ * Returns this list as a JSON string.
196
+ */
197
+ toString(): string;
198
+ /**
199
+ * Iterates over detached copies of the current live values in index order.
200
+ */
201
+ [Symbol.iterator](): IterableIterator<T>;
202
+ /**
203
+ * Calls a function once for each live value copy in index order.
204
+ *
205
+ * Callback values are detached copies, so mutating them does not mutate the
206
+ * list.
207
+ *
208
+ * @param callback - Function to call for each value copy.
209
+ * @param thisArg - Optional `this` value for the callback.
210
+ */
211
+ forEach(callback: (value: T, index: number, list: this) => void, thisArg?: unknown): void;
212
+ }
213
+
214
+ /**
215
+ * Error codes thrown by {@link CRList}.
216
+ */
217
+ type CRListErrorCode = 'VALUE_NOT_CLONEABLE' | 'INDEX_OUT_OF_BOUNDS' | 'LIST_EMPTY' | 'LIST_INTEGRITY_VIOLATION' | 'UPDATE_EXPECTED_AN_ARRAY';
218
+
98
219
  /**
99
220
  * Creates a local CRList replica from an optional snapshot.
100
221
  *
@@ -112,17 +233,20 @@ type CRListEventListenerFor<T, K extends string> = K extends keyof CRListEventMa
112
233
  *
113
234
  * Space complexity: O(n + t + c)
114
235
  */
115
- declare function __create<T>(snapshot?: CRListSnapshot<T>): CRListReplica<T>;
236
+ declare function __create<T>(snapshot?: CRListSnapshot<T>): CRListState<T>;
116
237
 
117
238
  /**
118
239
  * Reads the value at an index in the replica live view.
119
240
  *
120
- * The replica cursor is moved as part of the lookup. Out-of-bounds and empty
121
- * list reads resolve to `undefined` instead of throwing.
241
+ * The replica cursor is moved as part of the lookup. Successful reads return a
242
+ * detached structured clone of the visible value, so mutating the returned
243
+ * value does not mutate the replica itself. Out-of-bounds and empty list reads
244
+ * resolve to `undefined` instead of throwing.
122
245
  *
123
246
  * @param targetIndex Index in the live list.
124
247
  * @param crListReplica Replica to read from.
125
- * @returns The value at `targetIndex`, or `undefined` when no value is present.
248
+ * @returns A detached copy of the value at `targetIndex`, or `undefined` when
249
+ * no value is present.
126
250
  *
127
251
  * Time complexity: O(d), worst case O(n)
128
252
  * - d = distance from cursor to target index
@@ -130,7 +254,7 @@ declare function __create<T>(snapshot?: CRListSnapshot<T>): CRListReplica<T>;
130
254
  *
131
255
  * Space complexity: O(1)
132
256
  */
133
- declare function __read<T>(targetIndex: number, crListReplica: CRListReplica<T>): T | undefined;
257
+ declare function __read<T>(targetIndex: number, crListReplica: CRListState<T>): T | undefined;
134
258
 
135
259
  /**
136
260
  * Applies a local value mutation to the replica live view.
@@ -154,7 +278,7 @@ declare function __read<T>(targetIndex: number, crListReplica: CRListReplica<T>)
154
278
  *
155
279
  * Space complexity: O(v + c)
156
280
  */
157
- declare function __update<T>(listIndex: number, listValues: Array<T>, crListReplica: CRListReplica<T>, mode: 'overwrite' | 'before' | 'after'): {
281
+ declare function __update<T>(listIndex: number, listValues: Array<T>, crListReplica: CRListState<T>, mode: 'overwrite' | 'before' | 'after'): {
158
282
  change: CRListChange<T>;
159
283
  delta: CRListDelta<T>;
160
284
  } | false;
@@ -179,7 +303,7 @@ declare function __update<T>(listIndex: number, listValues: Array<T>, crListRepl
179
303
  *
180
304
  * Space complexity: O(q)
181
305
  */
182
- declare function __delete<T>(crListReplica: CRListReplica<T>, startIndex?: number, endIndex?: number): {
306
+ declare function __delete<T>(crListReplica: CRListState<T>, startIndex?: number, endIndex?: number): {
183
307
  change: CRListChange<T>;
184
308
  delta: CRListDelta<T>;
185
309
  } | false;
@@ -207,7 +331,7 @@ declare function __delete<T>(crListReplica: CRListReplica<T>, startIndex?: numbe
207
331
  *
208
332
  * Space complexity: O(n + v + t + c)
209
333
  */
210
- declare function __merge<T>(crListReplica: CRListReplica<T>, crListDelta: CRListDelta<T>): CRListChange<T> | false;
334
+ declare function __merge<T>(crListReplica: CRListState<T>, crListDelta: CRListDelta<T>): CRListChange<T> | false;
211
335
 
212
336
  /**
213
337
  * Returns the replica tombstone acknowledgement frontier.
@@ -223,7 +347,7 @@ declare function __merge<T>(crListReplica: CRListReplica<T>, crListDelta: CRList
223
347
  *
224
348
  * Space complexity: O(1)
225
349
  */
226
- declare function __acknowledge<T>(crListReplica: CRListReplica<T>): CRListAck | false;
350
+ declare function __acknowledge<T>(crListReplica: CRListState<T>): CRListAck | false;
227
351
 
228
352
  /**
229
353
  * Removes tombstones acknowledged by all supplied frontiers.
@@ -240,7 +364,7 @@ declare function __acknowledge<T>(crListReplica: CRListReplica<T>): CRListAck |
240
364
  *
241
365
  * Space complexity: O(1)
242
366
  */
243
- declare function __garbageCollect<T>(frontiers: Array<CRListAck>, crListReplica: CRListReplica<T>): void;
367
+ declare function __garbageCollect<T>(frontiers: Array<CRListAck>, crListReplica: CRListState<T>): void;
244
368
 
245
369
  /**
246
370
  * Creates a full serializable CRList snapshot from the current replica state.
@@ -258,123 +382,6 @@ declare function __garbageCollect<T>(frontiers: Array<CRListAck>, crListReplica:
258
382
  *
259
383
  * Space complexity: O(n + t + c)
260
384
  */
261
- declare function __snapshot<T>(crListReplica: CRListReplica<T>): CRListSnapshot<T>;
262
-
263
- /**
264
- * Error codes thrown by {@link CRList}.
265
- */
266
- type CRListErrorCode = 'VALUE_NOT_CLONEABLE' | 'INDEX_OUT_OF_BOUNDS' | 'LIST_EMPTY' | 'LIST_INTEGRITY_VIOLATION' | 'UPDATE_EXPECTED_AN_ARRAY';
267
-
268
- /**
269
- * A convergent replicated list.
270
- *
271
- * Numeric property access reads and mutates the live list projection:
272
- * `list[0]` reads an entry, `list[0] = value` writes an entry, and `delete
273
- * list[0]` removes one entry. Local mutations emit `delta` and `change` events;
274
- * remote merges emit `change` events.
275
- *
276
- * @typeParam T - The value type stored in the list.
277
- */
278
- declare class CRList<T> {
279
- /**
280
- * Reads or overwrites an entry in the live list projection by index.
281
- */
282
- [index: number]: T;
283
- private readonly state;
284
- private readonly eventTarget;
285
- /**
286
- * Creates a replicated list from an optional serializable snapshot.
287
- *
288
- * @param snapshot - A previously emitted CRList snapshot.
289
- */
290
- constructor(snapshot?: CRListSnapshot<T>);
291
- /**
292
- * The current number of live entries.
293
- */
294
- get size(): number;
295
- /**
296
- * Inserts a value before an index.
297
- *
298
- * If `beforeIndex` is omitted, the value is inserted at the start of the list.
299
- *
300
- * @param value - The value to insert.
301
- * @param beforeIndex - The index to insert before.
302
- */
303
- prepend(value: T, beforeIndex?: number): void;
304
- /**
305
- * Inserts a value after an index.
306
- *
307
- * If `afterIndex` is omitted, the value is appended at the end of the list.
308
- *
309
- * @param value - The value to insert.
310
- * @param afterIndex - The index to insert after.
311
- */
312
- append(value: T, afterIndex?: number): void;
313
- /**
314
- * Removes the entry at an index.
315
- *
316
- * @param index - The index to remove.
317
- */
318
- remove(index: number): void;
319
- /**
320
- * Applies a remote gossip delta to this list.
321
- *
322
- * Emits a `change` event when the merge changes the live projection.
323
- *
324
- * @param delta - The remote CRList delta to merge.
325
- */
326
- merge(delta: CRListDelta<T>): void;
327
- /**
328
- * Emits an acknowledgement frontier for currently retained tombstones.
329
- */
330
- acknowledge(): void;
331
- /**
332
- * Garbage-collects tombstones that are covered by acknowledgement frontiers.
333
- *
334
- * @param frontiers - Replica acknowledgement frontiers.
335
- */
336
- garbageCollect(frontiers: Array<CRListAck>): void;
337
- /**
338
- * Emits the current serializable list snapshot.
339
- */
340
- snapshot(): void;
341
- /**
342
- * Registers an event listener.
343
- *
344
- * @param type - The event type to listen for.
345
- * @param listener - The listener to register.
346
- * @param options - Listener registration options.
347
- */
348
- addEventListener<K extends keyof CRListEventMap<T>>(type: K, listener: CRListEventListenerFor<T, K> | null, options?: boolean | AddEventListenerOptions): void;
349
- /**
350
- * Removes an event listener.
351
- *
352
- * @param type - The event type to stop listening for.
353
- * @param listener - The listener to remove.
354
- * @param options - Listener removal options.
355
- */
356
- removeEventListener<K extends keyof CRListEventMap<T>>(type: K, listener: CRListEventListenerFor<T, K> | null, options?: boolean | EventListenerOptions): void;
357
- /**
358
- * Returns a serializable snapshot representation of this list.
359
- *
360
- * Called automatically by `JSON.stringify`.
361
- */
362
- toJSON(): CRListSnapshot<T>;
363
- /**
364
- * Returns this list as a JSON string.
365
- */
366
- toString(): string;
367
- /**
368
- * Iterates over the current live values in index order.
369
- */
370
- [Symbol.iterator](): IterableIterator<T>;
371
- /**
372
- * Calls a function once for each live value in index order.
373
- *
374
- * @param callback - Function to call for each value.
375
- * @param thisArg - Optional `this` value for the callback.
376
- */
377
- forEach(callback: (value: T, index: number, list: this) => void, thisArg?: unknown): void;
378
- }
385
+ declare function __snapshot<T>(crListReplica: CRListState<T>): CRListSnapshot<T>;
379
386
 
380
- export { CRList, type CRListAck, type CRListChange, type CRListDelta, type CRListErrorCode, type CRListReplica, type CRListSnapshot, __acknowledge, __create, __delete, __garbageCollect, __merge, __read, __snapshot, __update };
387
+ export { CRList, type CRListAck, type CRListChange, type CRListDelta, type CRListErrorCode, type CRListEventListener, type CRListEventListenerFor, type CRListEventMap, type CRListSnapshot, type CRListSnapshotEntry, type CRListState, type CRListStateEntry, __acknowledge, __create, __delete, __garbageCollect, __merge, __read, __snapshot, __update };
package/dist/index.js CHANGED
@@ -15,9 +15,6 @@
15
15
  */
16
16
 
17
17
 
18
- // src/core/crud/create/index.ts
19
- import { isUuidV7 as isUuidV72, prototype } from "@sovereignbase/utils";
20
-
21
18
  // src/.helpers/assertListIndices/index.ts
22
19
  function assertListIndices(crListReplica) {
23
20
  if (!crListReplica.cursor) return;
@@ -218,6 +215,7 @@ function indexFromPropertyKey(index) {
218
215
  }
219
216
 
220
217
  // src/core/crud/create/index.ts
218
+ import { isUuidV7 as isUuidV72, prototype } from "@sovereignbase/utils";
221
219
  function __create(snapshot) {
222
220
  const crListReplica = {
223
221
  size: 0,
@@ -253,7 +251,7 @@ function __create(snapshot) {
253
251
  function __read(targetIndex, crListReplica) {
254
252
  try {
255
253
  void walkToIndex(targetIndex, crListReplica);
256
- return crListReplica?.cursor?.value;
254
+ return structuredClone(crListReplica?.cursor?.value);
257
255
  } catch {
258
256
  return void 0;
259
257
  }
@@ -825,7 +823,7 @@ var CRList = class {
825
823
  return this.toJSON();
826
824
  }
827
825
  /**
828
- * Iterates over the current live values in index order.
826
+ * Iterates over detached copies of the current live values in index order.
829
827
  */
830
828
  *[Symbol.iterator]() {
831
829
  for (let index = 0; index < this.size; index++) {
@@ -834,9 +832,12 @@ var CRList = class {
834
832
  }
835
833
  }
836
834
  /**
837
- * Calls a function once for each live value in index order.
835
+ * Calls a function once for each live value copy in index order.
836
+ *
837
+ * Callback values are detached copies, so mutating them does not mutate the
838
+ * list.
838
839
  *
839
- * @param callback - Function to call for each value.
840
+ * @param callback - Function to call for each value copy.
840
841
  * @param thisArg - Optional `this` value for the callback.
841
842
  */
842
843
  forEach(callback, thisArg) {