@bradensbay/globals-core 0.2.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.
Files changed (98) hide show
  1. package/README.md +69 -0
  2. package/dist/src/allocator.d.ts +78 -0
  3. package/dist/src/allocator.d.ts.map +1 -0
  4. package/dist/src/allocator.js +211 -0
  5. package/dist/src/allocator.js.map +1 -0
  6. package/dist/src/arena.d.ts +81 -0
  7. package/dist/src/arena.d.ts.map +1 -0
  8. package/dist/src/arena.js +224 -0
  9. package/dist/src/arena.js.map +1 -0
  10. package/dist/src/checksum.d.ts +5 -0
  11. package/dist/src/checksum.d.ts.map +1 -0
  12. package/dist/src/checksum.js +35 -0
  13. package/dist/src/checksum.js.map +1 -0
  14. package/dist/src/draft.d.ts +48 -0
  15. package/dist/src/draft.d.ts.map +1 -0
  16. package/dist/src/draft.js +427 -0
  17. package/dist/src/draft.js.map +1 -0
  18. package/dist/src/errors.d.ts +56 -0
  19. package/dist/src/errors.d.ts.map +1 -0
  20. package/dist/src/errors.js +69 -0
  21. package/dist/src/errors.js.map +1 -0
  22. package/dist/src/external.d.ts +52 -0
  23. package/dist/src/external.d.ts.map +1 -0
  24. package/dist/src/external.js +68 -0
  25. package/dist/src/external.js.map +1 -0
  26. package/dist/src/hamt.d.ts +87 -0
  27. package/dist/src/hamt.d.ts.map +1 -0
  28. package/dist/src/hamt.js +643 -0
  29. package/dist/src/hamt.js.map +1 -0
  30. package/dist/src/history.d.ts +45 -0
  31. package/dist/src/history.d.ts.map +1 -0
  32. package/dist/src/history.js +49 -0
  33. package/dist/src/history.js.map +1 -0
  34. package/dist/src/index.d.ts +55 -0
  35. package/dist/src/index.d.ts.map +1 -0
  36. package/dist/src/index.js +38 -0
  37. package/dist/src/index.js.map +1 -0
  38. package/dist/src/inspect.d.ts +74 -0
  39. package/dist/src/inspect.d.ts.map +1 -0
  40. package/dist/src/inspect.js +127 -0
  41. package/dist/src/inspect.js.map +1 -0
  42. package/dist/src/layout.d.ts +130 -0
  43. package/dist/src/layout.d.ts.map +1 -0
  44. package/dist/src/layout.js +149 -0
  45. package/dist/src/layout.js.map +1 -0
  46. package/dist/src/liveness.d.ts +48 -0
  47. package/dist/src/liveness.d.ts.map +1 -0
  48. package/dist/src/liveness.js +92 -0
  49. package/dist/src/liveness.js.map +1 -0
  50. package/dist/src/owner.d.ts +130 -0
  51. package/dist/src/owner.d.ts.map +1 -0
  52. package/dist/src/owner.js +368 -0
  53. package/dist/src/owner.js.map +1 -0
  54. package/dist/src/reader.d.ts +122 -0
  55. package/dist/src/reader.d.ts.map +1 -0
  56. package/dist/src/reader.js +325 -0
  57. package/dist/src/reader.js.map +1 -0
  58. package/dist/src/readers.d.ts +60 -0
  59. package/dist/src/readers.d.ts.map +1 -0
  60. package/dist/src/readers.js +122 -0
  61. package/dist/src/readers.js.map +1 -0
  62. package/dist/src/retained.d.ts +39 -0
  63. package/dist/src/retained.d.ts.map +1 -0
  64. package/dist/src/retained.js +99 -0
  65. package/dist/src/retained.js.map +1 -0
  66. package/dist/src/schema.d.ts +82 -0
  67. package/dist/src/schema.d.ts.map +1 -0
  68. package/dist/src/schema.js +18 -0
  69. package/dist/src/schema.js.map +1 -0
  70. package/dist/src/store.d.ts +83 -0
  71. package/dist/src/store.d.ts.map +1 -0
  72. package/dist/src/store.js +127 -0
  73. package/dist/src/store.js.map +1 -0
  74. package/dist/src/strings.d.ts +37 -0
  75. package/dist/src/strings.d.ts.map +1 -0
  76. package/dist/src/strings.js +144 -0
  77. package/dist/src/strings.js.map +1 -0
  78. package/dist/src/tags.d.ts +47 -0
  79. package/dist/src/tags.d.ts.map +1 -0
  80. package/dist/src/tags.js +53 -0
  81. package/dist/src/tags.js.map +1 -0
  82. package/dist/src/values.d.ts +90 -0
  83. package/dist/src/values.d.ts.map +1 -0
  84. package/dist/src/values.js +458 -0
  85. package/dist/src/values.js.map +1 -0
  86. package/dist/src/vector.d.ts +64 -0
  87. package/dist/src/vector.d.ts.map +1 -0
  88. package/dist/src/vector.js +387 -0
  89. package/dist/src/vector.js.map +1 -0
  90. package/dist/src/verify.d.ts +38 -0
  91. package/dist/src/verify.d.ts.map +1 -0
  92. package/dist/src/verify.js +179 -0
  93. package/dist/src/verify.js.map +1 -0
  94. package/dist/src/view.d.ts +66 -0
  95. package/dist/src/view.d.ts.map +1 -0
  96. package/dist/src/view.js +278 -0
  97. package/dist/src/view.js.map +1 -0
  98. package/package.json +28 -0
@@ -0,0 +1,368 @@
1
+ import { SharedArena } from "./arena.js";
2
+ import { Allocator } from "./allocator.js";
3
+ import { ReaderTable } from "./readers.js";
4
+ import { RetainedRing } from "./retained.js";
5
+ import { StringTable } from "./strings.js";
6
+ import { Header, VerifyMode } from "./layout.js";
7
+ import { publishChecksum } from "./verify.js";
8
+ import { collectBlocks, decodeValue, encodeValue, } from "./values.js";
9
+ import { createDraft, finalizeState } from "./draft.js";
10
+ import { Tag } from "./tags.js";
11
+ import { GlobalsError } from "./errors.js";
12
+ const DEFAULTS = {
13
+ byteLength: 1 << 20,
14
+ maxByteLength: 1 << 26,
15
+ maxReaders: 32,
16
+ retainedVersions: 64,
17
+ verify: VerifyMode.Header,
18
+ historyDepth: 0,
19
+ };
20
+ /**
21
+ * The sole writer.
22
+ *
23
+ * In Electron this runs in the hidden owner window. In tests it runs on the main thread of
24
+ * a plain Node process, which is exactly the point of keeping this package free of Electron
25
+ * imports.
26
+ *
27
+ * The single writer property is load bearing. It removes allocator locking, it makes the
28
+ * free lists safe to keep on the writer heap where no other window can corrupt them, and it
29
+ * makes the commit sequence a plain store rather than a contended compare and exchange.
30
+ */
31
+ export class ArenaOwner {
32
+ arena;
33
+ #allocator;
34
+ #strings;
35
+ #readers;
36
+ #ring;
37
+ #versionId = 0;
38
+ #currentSlot = { tag: 0, payload: 0 };
39
+ /**
40
+ * The oldest version a reader may still decode. Monotonic: once memory below a version
41
+ * is freed, no later decision may lower the floor back over it.
42
+ */
43
+ #reclaimFloor = 0;
44
+ #historyDepth = 0;
45
+ #verify = VerifyMode.Header;
46
+ /**
47
+ * Blocks that became unreachable when a version was superseded, keyed by the version
48
+ * that still referenced them. Insertion order is ascending by version, which the
49
+ * reclamation scan relies on.
50
+ */
51
+ #garbage = new Map();
52
+ constructor(arena) {
53
+ this.arena = arena;
54
+ this.#allocator = new Allocator(arena, (minimum) => this.#growBuffer(minimum));
55
+ this.#strings = new StringTable(arena, this.#allocator);
56
+ this.#readers = new ReaderTable(arena);
57
+ this.#ring = new RetainedRing(arena);
58
+ }
59
+ /** Allocate and format a new arena. */
60
+ static create(options = {}) {
61
+ const settings = { ...DEFAULTS, ...options };
62
+ if (settings.maxByteLength < settings.byteLength) {
63
+ throw new GlobalsError("maxByteLength cannot be smaller than byteLength");
64
+ }
65
+ const buffer = new SharedArrayBuffer(settings.byteLength, {
66
+ maxByteLength: settings.maxByteLength,
67
+ });
68
+ const arena = SharedArena.format(buffer, {
69
+ maxReaders: settings.maxReaders,
70
+ retainedCapacity: settings.retainedVersions,
71
+ flags: settings.verify,
72
+ });
73
+ const owner = new ArenaOwner(arena);
74
+ owner.#historyDepth = Math.max(0, Math.min(settings.historyDepth, settings.retainedVersions - 1));
75
+ owner.#verify = settings.verify;
76
+ owner.commit(undefined);
77
+ return owner;
78
+ }
79
+ /**
80
+ * Adopt a buffer that a previous owner formatted. Bumps the owner generation so readers
81
+ * attached to the dead owner can detect the change.
82
+ *
83
+ * The free lists cannot be recovered, so an adopted arena starts with a bump pointer
84
+ * where the previous owner left it and no reusable blocks. That is acceptable because
85
+ * adoption follows an owner crash, which is rare and already costs a rehydrate.
86
+ */
87
+ static adopt(buffer) {
88
+ const arena = SharedArena.attach(buffer);
89
+ arena.addHeader(Header.OwnerGeneration, 1);
90
+ const owner = new ArenaOwner(arena);
91
+ owner.#verify = (arena.loadHeader(Header.Flags) & 0b11);
92
+ owner.#versionId = arena.loadHeader(Header.VersionId);
93
+ owner.#reclaimFloor = arena.loadHeader(Header.ReclaimFloor);
94
+ owner.#currentSlot = {
95
+ tag: arena.loadHeader(Header.RootTag),
96
+ payload: arena.loadHeader(Header.RootPayload),
97
+ };
98
+ return owner;
99
+ }
100
+ get buffer() {
101
+ return this.arena.buffer;
102
+ }
103
+ get versionId() {
104
+ return this.#versionId;
105
+ }
106
+ get strings() {
107
+ return this.#strings;
108
+ }
109
+ get allocator() {
110
+ return this.#allocator;
111
+ }
112
+ get readers() {
113
+ return this.#readers;
114
+ }
115
+ get ring() {
116
+ return this.#ring;
117
+ }
118
+ /**
119
+ * Encode `value`, publish it as the new root, and reclaim what the previous version was
120
+ * keeping alive.
121
+ *
122
+ * The publication is a seqlock write. Readers never block on it: they observe an odd
123
+ * sequence, spin briefly, and retry. The window is a handful of stores wide.
124
+ */
125
+ commit(value) {
126
+ const context = this.#newContext();
127
+ let slot;
128
+ try {
129
+ slot = encodeValue(context, value);
130
+ }
131
+ catch (error) {
132
+ this.#rollback(context);
133
+ throw error;
134
+ }
135
+ // A wholesale replacement shares nothing with the previous version, so everything the
136
+ // old root reached is garbage once that version is unreadable.
137
+ const retired = collectBlocks(this.arena, this.#currentSlot, context.retired);
138
+ return this.#install(slot, retired);
139
+ }
140
+ /**
141
+ * Apply a recipe to a draft of the current state and commit the result.
142
+ *
143
+ * This is the path that makes retention affordable. Only the paths the recipe touched are
144
+ * rebuilt, so setting one field of a large object copies a handful of nodes rather than
145
+ * the tree.
146
+ *
147
+ * owner.update((draft) => {
148
+ * draft.users[3].name = "new name";
149
+ * });
150
+ */
151
+ update(recipe) {
152
+ const context = this.#newContext();
153
+ const current = this.#currentSlot;
154
+ if (current.tag !== Tag.Object && current.tag !== Tag.Array) {
155
+ throw new GlobalsError("update needs an object or array root. Commit one first, or use commit to replace " +
156
+ "the root outright.");
157
+ }
158
+ let slot;
159
+ try {
160
+ const draft = createDraft(context, current);
161
+ recipe(draft.proxy);
162
+ slot = finalizeState(context, draft.state);
163
+ }
164
+ catch (error) {
165
+ this.#rollback(context);
166
+ throw error;
167
+ }
168
+ if (slot.tag === current.tag && slot.payload === current.payload) {
169
+ // The recipe changed nothing. Publishing a version anyway would wake every window for
170
+ // no reason.
171
+ return this.#versionId;
172
+ }
173
+ return this.#install(slot, context.retired);
174
+ }
175
+ /**
176
+ * The current state as a detached plain value.
177
+ *
178
+ * The owner reads through this rather than through a reader, because it already knows the
179
+ * current root and does not need to pin anything: nothing can reclaim a version while the
180
+ * only writer is inside a synchronous method.
181
+ */
182
+ readSnapshot() {
183
+ return decodeValue(this.arena, this.#currentSlot);
184
+ }
185
+ /**
186
+ * Undo a commit that failed part way.
187
+ *
188
+ * Both lists matter. The allocated blocks are obvious. The interning journal is less so:
189
+ * strings are append only and never freed during normal operation, so without releasing
190
+ * the ones a rejected write created, a sequence of rejected writes would consume the arena
191
+ * permanently. A window that can request writes could then exhaust it with writes that
192
+ * were all refused.
193
+ */
194
+ #rollback(context) {
195
+ for (const offset of context.allocated)
196
+ this.#allocator.free(offset);
197
+ this.#strings.forget(context.interned);
198
+ // Freeing returns the blocks to their size classes, which is not enough on its own: a
199
+ // write that filled the arena with small records leaves it unable to serve a larger
200
+ // request, because there is no coalescing. Rewinding the bump pointer past everything
201
+ // this commit allocated returns the arena to exactly where it was.
202
+ this.#allocator.rewindTo(context.bumpBefore);
203
+ }
204
+ #newContext() {
205
+ return {
206
+ arena: this.arena,
207
+ allocator: this.#allocator,
208
+ strings: this.#strings,
209
+ allocated: [],
210
+ retired: [],
211
+ interned: [],
212
+ bumpBefore: this.#allocator.stats().bumpPointer,
213
+ };
214
+ }
215
+ #install(slot, retired) {
216
+ const previousVersion = this.#versionId;
217
+ const version = previousVersion + 1;
218
+ this.#evictForVersion(version);
219
+ this.#publish(version, slot);
220
+ if (previousVersion > 0 && retired.length > 0) {
221
+ const existing = this.#garbage.get(previousVersion);
222
+ if (existing)
223
+ existing.push(...retired);
224
+ else
225
+ this.#garbage.set(previousVersion, retired);
226
+ }
227
+ this.#currentSlot = slot;
228
+ this.#versionId = version;
229
+ this.arena.addHeader(Header.StatCommits, 1);
230
+ this.reclaim();
231
+ return version;
232
+ }
233
+ #publish(version, slot) {
234
+ const arena = this.arena;
235
+ // Odd sequence: a commit is in progress and readers must retry.
236
+ arena.addHeader(Header.Sequence, 1);
237
+ // The ring entry is written before the header so a reader that observes the new version
238
+ // can always find it live in the ring.
239
+ this.#ring.publish(version, slot.tag, slot.payload);
240
+ arena.storeHeader(Header.RootTag, slot.tag);
241
+ arena.storeHeader(Header.RootPayload, slot.payload);
242
+ arena.storeHeader(Header.VersionId, version);
243
+ // The checksum goes inside the seqlock too, so a reader that observes the new version
244
+ // always observes the checksum that matches it rather than the previous one.
245
+ publishChecksum(arena, slot, version, this.#verify);
246
+ // Even sequence: the root is stable again.
247
+ arena.addHeader(Header.Sequence, 1);
248
+ }
249
+ /**
250
+ * Reclaim every version that no reader can still be pinned to.
251
+ *
252
+ * The order below is the whole correctness argument, so it is written out rather than
253
+ * left to be rediscovered:
254
+ *
255
+ * 1. Scan the reader table for the minimum pinned epoch.
256
+ * 2. Publish the resulting floor.
257
+ * 3. Scan again.
258
+ * 4. Free only below the minimum of the two scans.
259
+ *
260
+ * Step 3 is what closes the race. A reader that pinned between steps 1 and 2 is seen by
261
+ * the second scan. A reader that pinned after step 2 must load the floor after the store
262
+ * that published it, sees its version is below the floor, and retries. There is no
263
+ * interleaving in which a reader proceeds with a version this method then frees.
264
+ */
265
+ reclaim() {
266
+ const firstScan = this.#readers.minimumPinnedEpoch();
267
+ // History, when it is switched on, holds versions alive that nothing is pinned to. It is
268
+ // a deliberate cost rather than a free feature: those versions keep their path copies.
269
+ const boundary = Math.min(firstScan, this.#versionId - this.#historyDepth);
270
+ this.#raiseFloor(Number.isFinite(boundary) ? boundary : 0);
271
+ const secondScan = this.#readers.minimumPinnedEpoch();
272
+ const safeBelow = Math.min(boundary, secondScan);
273
+ for (const [versionId, blocks] of this.#garbage) {
274
+ if (versionId >= safeBelow)
275
+ break;
276
+ for (const offset of blocks)
277
+ this.#allocator.free(offset);
278
+ this.#garbage.delete(versionId);
279
+ this.#ring.retire(versionId, false);
280
+ }
281
+ this.arena.storeHeader(Header.StatBytesLive, this.#allocator.stats().liveBytes);
282
+ }
283
+ /**
284
+ * Raise the reclaim floor and publish it. Never lowers it: a reader that has been told a
285
+ * version is gone must not later be told it is available again, because the memory
286
+ * beneath it has already been handed to a different allocation.
287
+ */
288
+ #raiseFloor(value) {
289
+ if (value <= this.#reclaimFloor)
290
+ return;
291
+ this.#reclaimFloor = value;
292
+ this.arena.storeHeader(Header.ReclaimFloor, value);
293
+ }
294
+ /**
295
+ * The ring slot a new version will occupy may still hold a live version. Free it, even if
296
+ * a reader is pinned to it.
297
+ *
298
+ * This is the bounded retention rule. The alternative is to let one frozen window grow
299
+ * the arena without limit, which turns a stuck renderer into an out of memory crash for
300
+ * the whole application. A reader that loses its version this way raises
301
+ * StaleSnapshotError on its next decode and reacquires, which is a recoverable outcome.
302
+ */
303
+ #evictForVersion(version) {
304
+ const evicted = version - this.#ring.capacity;
305
+ if (evicted < 1)
306
+ return;
307
+ const minimumPinned = this.#readers.minimumPinnedEpoch();
308
+ if (minimumPinned <= evicted)
309
+ this.arena.addHeader(Header.StatForcedAdvances, 1);
310
+ // The floor rises before anything is freed. A reader that is part way through decoding
311
+ // one of these versions revalidates after its decode and sees the raised floor, so it
312
+ // reports a stale snapshot instead of returning a value read from reused memory. The
313
+ // reverse order loses that: the reader would decode recycled bytes and revalidate
314
+ // against a floor that had not moved yet.
315
+ this.#raiseFloor(evicted + 1);
316
+ for (const [versionId, blocks] of this.#garbage) {
317
+ if (versionId > evicted)
318
+ break;
319
+ for (const offset of blocks)
320
+ this.#allocator.free(offset);
321
+ this.#garbage.delete(versionId);
322
+ this.#ring.retire(versionId, versionId >= minimumPinned);
323
+ }
324
+ }
325
+ /**
326
+ * Grow the buffer. Every holder observes the new length, because a growable
327
+ * SharedArrayBuffer reserves its maximum at allocation and grow() only publishes more of
328
+ * it. Views made before the growth are refreshed lazily by SharedArena.
329
+ */
330
+ #growBuffer(minimumBytes) {
331
+ const buffer = this.arena.buffer;
332
+ const maximum = buffer.maxByteLength ?? buffer.byteLength;
333
+ if (!buffer.growable || buffer.byteLength >= maximum)
334
+ return false;
335
+ const wanted = Math.min(maximum, Math.max(buffer.byteLength * 2, buffer.byteLength + minimumBytes));
336
+ if (wanted <= buffer.byteLength)
337
+ return false;
338
+ buffer.grow(wanted);
339
+ this.arena.refresh();
340
+ this.arena.storeHeader(Header.CapacityBytes, this.arena.byteLength);
341
+ return true;
342
+ }
343
+ /** Bump the owner heartbeat. Readers use it to detect an owner that has stopped. */
344
+ beat() {
345
+ this.arena.addHeader(Header.OwnerHeartbeat, 1);
346
+ }
347
+ stats() {
348
+ const allocator = this.#allocator.stats();
349
+ const minimum = this.#readers.minimumPinnedEpoch();
350
+ return {
351
+ versionId: this.#versionId,
352
+ commits: this.arena.loadHeader(Header.StatCommits),
353
+ forcedAdvances: this.arena.loadHeader(Header.StatForcedAdvances),
354
+ liveBytes: allocator.liveBytes,
355
+ bumpPointer: allocator.bumpPointer,
356
+ capacityBytes: allocator.capacityBytes,
357
+ freeListBytes: allocator.freeListBytes,
358
+ strandedBytes: this.#allocator.strandedBytes(),
359
+ internedStrings: this.#strings.size,
360
+ pendingGarbageVersions: this.#garbage.size,
361
+ historyDepth: this.#historyDepth,
362
+ reclaimFloor: this.arena.loadHeader(Header.ReclaimFloor),
363
+ claimedReaders: this.#readers.claimedSlots().length,
364
+ minimumPinnedEpoch: Number.isFinite(minimum) ? minimum : 0,
365
+ };
366
+ }
367
+ }
368
+ //# sourceMappingURL=owner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner.js","sourceRoot":"","sources":["../../src/owner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAwB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,GAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA6B3C,MAAM,QAAQ,GAAG;IACf,UAAU,EAAE,CAAC,IAAI,EAAE;IACnB,aAAa,EAAE,CAAC,IAAI,EAAE;IACtB,UAAU,EAAE,EAAE;IACd,gBAAgB,EAAE,EAAE;IACpB,MAAM,EAAE,UAAU,CAAC,MAAM;IACzB,YAAY,EAAE,CAAC;CACiB,CAAC;AAmBnC;;;;;;;;;;GAUG;AACH,MAAM,OAAO,UAAU;IACZ,KAAK,CAAc;IACnB,UAAU,CAAY;IACtB,QAAQ,CAAc;IACtB,QAAQ,CAAc;IACtB,KAAK,CAAe;IAE7B,UAAU,GAAG,CAAC,CAAC;IACf,YAAY,GAAS,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC5C;;;OAGG;IACH,aAAa,GAAG,CAAC,CAAC;IAClB,aAAa,GAAG,CAAC,CAAC;IAClB,OAAO,GAAoB,UAAU,CAAC,MAAM,CAAC;IAC7C;;;;OAIG;IACM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEhD,YAAoB,KAAkB;QACpC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,UAAU,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,QAAQ,GAAG,IAAI,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,uCAAuC;IACvC,MAAM,CAAC,MAAM,CAAC,OAAO,GAAiB,EAAE;QACtC,MAAM,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;QAC7C,IAAI,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC;YACjD,MAAM,IAAI,YAAY,CAAC,iDAAiD,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,UAAU,EAAE;YACxD,aAAa,EAAE,QAAQ,CAAC,aAAa;SACtC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE;YACvC,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YAC3C,KAAK,EAAE,QAAQ,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;QACpC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAC5B,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAC/D,CAAC;QACF,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC;QAChC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,CAAC,MAAyB;QACpC,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;QACpC,KAAK,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAoB,CAAC;QAC3E,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtD,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC5D,KAAK,CAAC,YAAY,GAAG;YACnB,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC;YACrC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC;SAC9C,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,KAAc;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnC,IAAI,IAAU,CAAC;QACf,IAAI,CAAC;YACH,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACxB,MAAM,KAAK,CAAC;QACd,CAAC;QAED,sFAAsF;QACtF,+DAA+D;QAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAI,MAA0B;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC;QAElC,IAAI,OAAO,CAAC,GAAG,KAAK,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC;YAC5D,MAAM,IAAI,YAAY,CACpB,mFAAmF;gBACjF,oBAAoB,CACvB,CAAC;QACJ,CAAC;QAED,IAAI,IAAU,CAAC;QACf,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,KAAU,CAAC,CAAC;YACzB,IAAI,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACxB,MAAM,KAAK,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;YACjE,sFAAsF;YACtF,aAAa;YACb,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACH,YAAY;QACV,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;;OAQG;IACH,SAAS,CAAC,OAAsB;QAC9B,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvC,sFAAsF;QACtF,oFAAoF;QACpF,sFAAsF;QACtF,mEAAmE;QACnE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED,WAAW;QACT,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,SAAS,EAAE,EAAE;YACb,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,EAAE;YACZ,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,WAAW;SAChD,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,IAAU,EAAE,OAAiB;QACpC,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC;QACxC,MAAM,OAAO,GAAG,eAAe,GAAG,CAAC,CAAC;QAEpC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAE7B,IAAI,eAAe,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YACpD,IAAI,QAAQ;gBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;;gBACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,QAAQ,CAAC,OAAe,EAAE,IAAU;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,gEAAgE;QAChE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACpC,wFAAwF;QACxF,uCAAuC;QACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5C,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC7C,sFAAsF;QACtF,6EAA6E;QAC7E,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,2CAA2C;QAC3C,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,OAAO;QACL,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QACrD,yFAAyF;QACzF,uFAAuF;QACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3E,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE3D,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEjD,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChD,IAAI,SAAS,IAAI,SAAS;gBAAE,MAAM;YAClC,KAAK,MAAM,MAAM,IAAI,MAAM;gBAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC;IAClF,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAa;QACvB,IAAI,KAAK,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QACxC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IAED;;;;;;;;OAQG;IACH,gBAAgB,CAAC,OAAe;QAC9B,MAAM,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC9C,IAAI,OAAO,GAAG,CAAC;YAAE,OAAO;QAExB,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QACzD,IAAI,aAAa,IAAI,OAAO;YAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;QAEjF,uFAAuF;QACvF,sFAAsF;QACtF,qFAAqF;QACrF,kFAAkF;QAClF,0CAA0C;QAC1C,IAAI,CAAC,WAAW,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;QAE9B,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChD,IAAI,SAAS,GAAG,OAAO;gBAAE,MAAM;YAC/B,KAAK,MAAM,MAAM,IAAI,MAAM;gBAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,IAAI,aAAa,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,YAAoB;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QACjC,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,UAAU,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,UAAU,IAAI,OAAO;YAAE,OAAO,KAAK,CAAC;QAEnE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,OAAO,EACP,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,YAAY,CAAC,CAClE,CAAC;QACF,IAAI,MAAM,IAAI,MAAM,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAE9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oFAAoF;IACpF,IAAI;QACF,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,KAAK;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC;QACnD,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC;YAClD,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,kBAAkB,CAAC;YAChE,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,aAAa,EAAE,SAAS,CAAC,aAAa;YACtC,aAAa,EAAE,SAAS,CAAC,aAAa;YACtC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE;YAC9C,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;YACnC,sBAAsB,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;YAC1C,YAAY,EAAE,IAAI,CAAC,aAAa;YAChC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC;YACxD,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM;YACnD,kBAAkB,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;SAC3D,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,122 @@
1
+ import { SharedArena } from "./arena.js";
2
+ import { type Slot } from "./values.js";
3
+ export interface SnapshotInfo {
4
+ readonly versionId: number;
5
+ readonly ownerGeneration: number;
6
+ }
7
+ /**
8
+ * A pinned version.
9
+ *
10
+ * Holding one guarantees the graph beneath it cannot change, because the writer never
11
+ * mutates a published node. It does not guarantee the version stays retained forever: past
12
+ * the retention cap the owner force advances, and the next decode raises
13
+ * StaleSnapshotError rather than reading recycled memory.
14
+ */
15
+ export declare class Snapshot {
16
+ #private;
17
+ readonly versionId: number;
18
+ readonly ownerGeneration: number;
19
+ constructor(reader: ArenaReader, versionId: number, ownerGeneration: number, slot: Slot);
20
+ get released(): boolean;
21
+ /**
22
+ * The root value. Synchronous, no await, no round trip. This is the whole product.
23
+ *
24
+ * Validated on both sides of the decode. The check before it rejects a version that is
25
+ * already gone. The check after it is the one that matters: the owner raises the reclaim
26
+ * floor before it frees anything, so a version that is still above the floor once the
27
+ * decode has finished cannot have been freed while the decode was running. Validating
28
+ * only beforehand leaves a window in which a forced reclaim recycles the block and the
29
+ * reader returns a well formed but wrong value, which the soak harness found in about two
30
+ * reads out of four million.
31
+ */
32
+ get value(): unknown;
33
+ /**
34
+ * The whole version as a detached plain value, with no proxies and no arena references.
35
+ *
36
+ * Use it to hand state to something that has to own it, such as a serialiser or a worker.
37
+ * It costs a full decode, which is exactly what the lazy path exists to avoid, so it is a
38
+ * deliberate call rather than the default.
39
+ */
40
+ toJSON(): unknown;
41
+ /**
42
+ * Read one path without building views for the nodes along it.
43
+ *
44
+ * The cheapest shape for a selector, and what the framework bindings use.
45
+ */
46
+ get(path: readonly (string | number)[]): unknown;
47
+ /** The root slot, for callers decoding lazily rather than eagerly. */
48
+ get root(): Slot;
49
+ /**
50
+ * Prove this version is still retained. Two atomic loads and two comparisons, run on both
51
+ * sides of every decode. Cheap enough to sit on the read path, and the only thing standing
52
+ * between a stalled reader and freed memory.
53
+ */
54
+ validate(): void;
55
+ /**
56
+ * True when this snapshot can still be decoded. Never throws, and never constructs an
57
+ * error object, because it runs twice per read and a try/catch around the throwing form
58
+ * kept the whole path out of the optimising compiler.
59
+ */
60
+ isValid(): boolean;
61
+ release(): void;
62
+ }
63
+ export interface ReaderOptions {
64
+ /** Bump the heartbeat on every acquire so the owner can tell this reader is alive. */
65
+ heartbeat?: boolean;
66
+ }
67
+ /**
68
+ * A reader.
69
+ *
70
+ * Attaching claims one slot in the reader table. Acquiring a snapshot is a seqlock read of
71
+ * the root, one atomic store to publish the pinned epoch, and one atomic load to confirm
72
+ * the version is above the reclaim floor. Nothing here blocks, allocates a promise, or
73
+ * sends a message.
74
+ */
75
+ export declare class ArenaReader {
76
+ #private;
77
+ readonly arena: SharedArena;
78
+ readonly slot: number;
79
+ readonly generation: number;
80
+ private constructor();
81
+ static attach(buffer: SharedArrayBuffer, options?: ReaderOptions): ArenaReader;
82
+ get detached(): boolean;
83
+ reclaimFloor(): number;
84
+ ownerGeneration(): number;
85
+ /** The version the owner has currently published, without pinning it. */
86
+ publishedVersion(): number;
87
+ /**
88
+ * Acquire the current version and pin it.
89
+ *
90
+ * Returns the snapshot already held when the owner has not committed since, so a render
91
+ * loop that calls this every frame does not churn pins.
92
+ */
93
+ acquire(): Snapshot;
94
+ /** Read the current root value. The synchronous read the whole library exists for. */
95
+ read(): unknown;
96
+ /**
97
+ * Pin a specific retained version rather than the current one.
98
+ *
99
+ * For debugging and time travel. It pins that version and everything after it against
100
+ * reclamation, so a snapshot obtained this way must be released. A render path should
101
+ * never use it.
102
+ *
103
+ * A reader owns one epoch slot, so this suspends the pin the render path was holding until
104
+ * the returned snapshot is released. A debug panel that browses history while a render
105
+ * reads should attach its own reader rather than share one, which costs a slot and keeps
106
+ * the two independent.
107
+ */
108
+ acquireVersion(versionId: number): Snapshot;
109
+ /** Materialise a retained version, pinning it only for the duration of the read. */
110
+ readVersion(versionId: number): unknown;
111
+ releaseSnapshot(snapshot: Snapshot): void;
112
+ /** Release the pin and the reader slot. Always call this before dropping a reader. */
113
+ detach(): void;
114
+ stats(): {
115
+ slot: number;
116
+ generation: number;
117
+ pinnedEpoch: number;
118
+ publishedVersion: number;
119
+ reclaimFloor: number;
120
+ };
121
+ }
122
+ //# sourceMappingURL=reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKzC,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAWrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,qBAAa,QAAQ;;IACnB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAMjC,YAAY,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAKtF;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;;;;;;;OAUG;IACH,IAAI,KAAK,IAAI,OAAO,CAKnB;IAED;;;;;;OAMG;IACH,MAAM,IAAI,OAAO,CAKhB;IAED;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAK/C;IAmBD,sEAAsE;IACtE,IAAI,IAAI,IAAI,IAAI,CAGf;IAED;;;;OAIG;IACH,QAAQ,IAAI,IAAI,CAGf;IAED;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAMjB;IAED,OAAO,IAAI,IAAI,CAId;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW;;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAgB5B,OAAO,eASN;IAED,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,aAAkB,GAAG,WAAW,CAEjF;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,YAAY,IAAI,MAAM,CAErB;IAED,eAAe,IAAI,MAAM,CAExB;IAED,yEAAyE;IACzE,gBAAgB,IAAI,MAAM,CAEzB;IAED;;;;;OAKG;IACH,OAAO,IAAI,QAAQ,CAwDlB;IAED,sFAAsF;IACtF,IAAI,IAAI,OAAO,CAEd;IAED;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAsB1C;IAED,oFAAoF;IACpF,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAWtC;IAED,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAcxC;IAED,sFAAsF;IACtF,MAAM,IAAI,IAAI,CAKb;IAED,KAAK,IAAI;QACP,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,YAAY,EAAE,MAAM,CAAC;KACtB,CAQA;CACF"}