@twoabove/cue 0.4.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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +410 -0
  3. package/dist/api/create.d.ts +3 -0
  4. package/dist/api/create.d.ts.map +1 -0
  5. package/dist/api/create.js +106 -0
  6. package/dist/api/define.d.ts +40 -0
  7. package/dist/api/define.d.ts.map +1 -0
  8. package/dist/api/define.js +61 -0
  9. package/dist/api/index.d.ts +5 -0
  10. package/dist/api/index.d.ts.map +1 -0
  11. package/dist/api/index.js +7 -0
  12. package/dist/api/supervisor.d.ts +22 -0
  13. package/dist/api/supervisor.d.ts.map +1 -0
  14. package/dist/api/supervisor.js +39 -0
  15. package/dist/core/Evolution.d.ts +11 -0
  16. package/dist/core/Evolution.d.ts.map +1 -0
  17. package/dist/core/Evolution.js +29 -0
  18. package/dist/core/StateKernel.d.ts +35 -0
  19. package/dist/core/StateKernel.d.ts.map +1 -0
  20. package/dist/core/StateKernel.js +113 -0
  21. package/dist/errors/index.d.ts +22 -0
  22. package/dist/errors/index.d.ts.map +1 -0
  23. package/dist/errors/index.js +43 -0
  24. package/dist/index.d.ts +5 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +2 -0
  27. package/dist/persistence/adapters/inMemory.d.ts +21 -0
  28. package/dist/persistence/adapters/inMemory.d.ts.map +1 -0
  29. package/dist/persistence/adapters/inMemory.js +41 -0
  30. package/dist/persistence/types.d.ts +28 -0
  31. package/dist/persistence/types.d.ts.map +1 -0
  32. package/dist/persistence/types.js +1 -0
  33. package/dist/runtime/Entity.d.ts +42 -0
  34. package/dist/runtime/Entity.d.ts.map +1 -0
  35. package/dist/runtime/Entity.js +357 -0
  36. package/dist/runtime/EntityManager.d.ts +15 -0
  37. package/dist/runtime/EntityManager.d.ts.map +1 -0
  38. package/dist/runtime/EntityManager.js +46 -0
  39. package/dist/runtime/Mailbox.d.ts +5 -0
  40. package/dist/runtime/Mailbox.d.ts.map +1 -0
  41. package/dist/runtime/Mailbox.js +8 -0
  42. package/dist/runtime/Passivation.d.ts +12 -0
  43. package/dist/runtime/Passivation.d.ts.map +1 -0
  44. package/dist/runtime/Passivation.js +42 -0
  45. package/dist/runtime/Supervision.d.ts +4 -0
  46. package/dist/runtime/Supervision.d.ts.map +1 -0
  47. package/dist/runtime/Supervision.js +20 -0
  48. package/dist/serde/index.d.ts +7 -0
  49. package/dist/serde/index.d.ts.map +1 -0
  50. package/dist/serde/index.js +19 -0
  51. package/dist/types/internal.d.ts +10 -0
  52. package/dist/types/internal.d.ts.map +1 -0
  53. package/dist/types/internal.js +10 -0
  54. package/dist/types/public.d.ts +168 -0
  55. package/dist/types/public.d.ts.map +1 -0
  56. package/dist/types/public.js +1 -0
  57. package/dist/utils/clock.d.ts +5 -0
  58. package/dist/utils/clock.d.ts.map +1 -0
  59. package/dist/utils/clock.js +3 -0
  60. package/dist/utils/id.d.ts +2 -0
  61. package/dist/utils/id.d.ts.map +1 -0
  62. package/dist/utils/id.js +2 -0
  63. package/dist/utils/invariants.d.ts +2 -0
  64. package/dist/utils/invariants.d.ts.map +1 -0
  65. package/dist/utils/invariants.js +5 -0
  66. package/package.json +71 -0
@@ -0,0 +1,357 @@
1
+ import { applyPatches } from "immer";
2
+ import { Evolution } from "../core/Evolution";
3
+ import { StateKernel } from "../core/StateKernel";
4
+ import { CommitError, DefinitionMismatchError, HydrationError, OutOfOrderEventsError, StoppedEntityError, } from "../errors";
5
+ import { clone, deserialize, serialize } from "../serde";
6
+ import { _handlers, _initialStateFn, _name, _persistence, _upcasters, } from "../types/internal";
7
+ import { WallClock } from "../utils/clock";
8
+ import { Mailbox } from "./Mailbox";
9
+ import { Supervise } from "./Supervision";
10
+ export class Entity {
11
+ id;
12
+ def;
13
+ managerId;
14
+ store;
15
+ supervisor;
16
+ metrics;
17
+ clock;
18
+ kernel;
19
+ status = "pending";
20
+ mailbox = new Mailbox();
21
+ lastTouch;
22
+ error;
23
+ hydrationPromise;
24
+ constructor(id, def, managerId, store, supervisor, metrics, clock = WallClock) {
25
+ this.id = id;
26
+ this.def = def;
27
+ this.managerId = managerId;
28
+ this.store = store;
29
+ this.supervisor = supervisor;
30
+ this.metrics = metrics;
31
+ this.clock = clock;
32
+ this.kernel = new StateKernel(this.def);
33
+ this.lastTouch = this.clock.now();
34
+ }
35
+ get isFailed() {
36
+ return this.status === "failed";
37
+ }
38
+ get isShutdown() {
39
+ return this.status === "stopped";
40
+ }
41
+ get lastActivity() {
42
+ return this.lastTouch;
43
+ }
44
+ tell = (handlerName, args) => {
45
+ return this.mailbox.enqueue(async () => {
46
+ await this.ensureActive();
47
+ const task = async () => {
48
+ const entry = this.def[_handlers][handlerName];
49
+ if (entry?.type === "stream") {
50
+ const stream = this.kernel.startStream(handlerName, args, this.buildContext());
51
+ let finalReturn;
52
+ try {
53
+ while (true) {
54
+ const r = await stream.generator.next();
55
+ if (r.done) {
56
+ finalReturn = r.value;
57
+ break;
58
+ }
59
+ }
60
+ const { patches, nextState } = stream.finalize();
61
+ if (patches.length > 0) {
62
+ await this.commit(handlerName, args, finalReturn, patches, nextState);
63
+ }
64
+ }
65
+ catch (e) {
66
+ stream.discard();
67
+ throw e;
68
+ }
69
+ return finalReturn;
70
+ }
71
+ const { returnValue, patches, nextState } = await this.kernel.applyCommand(handlerName, args, this.buildContext());
72
+ if (patches.length > 0) {
73
+ await this.commit(handlerName, args, returnValue, patches, nextState);
74
+ }
75
+ return returnValue;
76
+ };
77
+ if (this.supervisor) {
78
+ return Supervise(task, this.kernel.state, this.supervisor, this.onReset, this.onStop);
79
+ }
80
+ return task();
81
+ });
82
+ };
83
+ ask = (handlerName, args) => {
84
+ return this.mailbox.enqueue(async () => {
85
+ await this.ensureActive();
86
+ return this.kernel.runQuery(handlerName, args, this.buildContext());
87
+ });
88
+ };
89
+ stream = (handlerName, args) => {
90
+ // Stream holds the mailbox for its entire duration to prevent interleaved
91
+ // commands from causing state overwrites when the stream commits.
92
+ const self = this;
93
+ const channel = [];
94
+ let consumerWaiting = null;
95
+ let producerWaiting = null;
96
+ let aborted = false;
97
+ function push(item) {
98
+ if (consumerWaiting) {
99
+ const resolve = consumerWaiting;
100
+ consumerWaiting = null;
101
+ resolve(item);
102
+ }
103
+ else {
104
+ channel.push(item);
105
+ }
106
+ }
107
+ function pull() {
108
+ const queued = channel.shift();
109
+ if (queued)
110
+ return Promise.resolve(queued);
111
+ return new Promise((resolve) => {
112
+ consumerWaiting = resolve;
113
+ });
114
+ }
115
+ function signalProducerContinue() {
116
+ if (producerWaiting) {
117
+ const { resolve } = producerWaiting;
118
+ producerWaiting = null;
119
+ resolve();
120
+ }
121
+ }
122
+ function signalProducerAbort() {
123
+ aborted = true;
124
+ if (producerWaiting) {
125
+ const { reject } = producerWaiting;
126
+ producerWaiting = null;
127
+ reject(new Error("stream aborted"));
128
+ }
129
+ }
130
+ const mailboxTask = self.mailbox.enqueue(async () => {
131
+ await self.ensureActive();
132
+ const stream = self.kernel.startStream(handlerName, args, self.buildContext());
133
+ try {
134
+ for await (const value of stream.generator) {
135
+ push({ type: "value", value });
136
+ await new Promise((resolve, reject) => {
137
+ producerWaiting = { resolve, reject };
138
+ });
139
+ }
140
+ push({ type: "done" });
141
+ }
142
+ catch (e) {
143
+ if (!aborted) {
144
+ stream.discard();
145
+ push({ type: "error", error: e });
146
+ return;
147
+ }
148
+ }
149
+ const { patches, nextState } = stream.finalize();
150
+ if (patches.length > 0) {
151
+ await self.commit(handlerName, args, undefined, patches, nextState);
152
+ }
153
+ });
154
+ async function* outerGenerator() {
155
+ try {
156
+ while (true) {
157
+ const item = await pull();
158
+ if (item.type === "done")
159
+ return;
160
+ if (item.type === "error")
161
+ throw item.error;
162
+ yield item.value;
163
+ signalProducerContinue();
164
+ }
165
+ }
166
+ finally {
167
+ signalProducerAbort();
168
+ await mailboxTask;
169
+ }
170
+ }
171
+ return outerGenerator();
172
+ };
173
+ inspect = async () => {
174
+ await this.ensureActive();
175
+ return {
176
+ state: clone(this.kernel.state),
177
+ version: this.kernel.version,
178
+ };
179
+ };
180
+ terminate = async () => {
181
+ if (this.isShutdown)
182
+ return;
183
+ await this.mailbox.enqueue(async () => {
184
+ this.status = "stopped";
185
+ });
186
+ };
187
+ stateAt = async (targetVersion) => {
188
+ if (!this.store) {
189
+ throw new Error("stateAt requires a persistence store");
190
+ }
191
+ const snapshotRec = await this.store.getLatestSnapshot(this.id);
192
+ let state;
193
+ let schemaVersion;
194
+ let currentVersion;
195
+ if (snapshotRec && snapshotRec.version <= targetVersion) {
196
+ const envelope = deserialize(snapshotRec.data);
197
+ state = envelope.state;
198
+ schemaVersion = envelope.schemaVersion;
199
+ currentVersion = snapshotRec.version;
200
+ }
201
+ else {
202
+ state = this.def[_initialStateFn]();
203
+ schemaVersion = 1;
204
+ currentVersion = 0n;
205
+ }
206
+ const events = await this.store.getEvents(this.id, currentVersion);
207
+ for (const eventRec of events) {
208
+ if (eventRec.version > targetVersion)
209
+ break;
210
+ const envelope = deserialize(eventRec.data);
211
+ if (envelope.schemaVersion > schemaVersion) {
212
+ state = Evolution.applyUpcastersTo(state, schemaVersion, envelope.schemaVersion, this.def);
213
+ schemaVersion = envelope.schemaVersion;
214
+ }
215
+ state = applyPatches(state, envelope.patches);
216
+ }
217
+ return {
218
+ schemaVersion,
219
+ state: clone(state),
220
+ };
221
+ };
222
+ async commit(handlerName, payload, returnVal, patches, nextState) {
223
+ if (!this.store) {
224
+ this.kernel.applyCommittedState(nextState);
225
+ return;
226
+ }
227
+ const newVersion = this.kernel.version + 1n;
228
+ const envelope = {
229
+ entityDefName: this.def[_name],
230
+ schemaVersion: this.def[_upcasters].length + 1,
231
+ handler: handlerName,
232
+ payload,
233
+ returnVal,
234
+ patches,
235
+ };
236
+ try {
237
+ await this.store.commitEvent(this.id, newVersion, serialize(envelope));
238
+ this.kernel.applyCommittedState(nextState);
239
+ this.metrics?.onAfterCommit?.(this.id, newVersion, patches);
240
+ await this.maybeSnapshot();
241
+ }
242
+ catch (err) {
243
+ this.status = "failed";
244
+ this.error = new CommitError("Failed to commit event", { cause: err });
245
+ throw this.error;
246
+ }
247
+ }
248
+ async ensureActive() {
249
+ this.lastTouch = this.clock.now();
250
+ if (this.status === "active")
251
+ return;
252
+ if (this.status === "hydrating")
253
+ return this.hydrationPromise;
254
+ if (this.status === "failed" || this.status === "stopped")
255
+ throw new StoppedEntityError(this.id);
256
+ if (this.status === "pending")
257
+ await this.hydrate();
258
+ }
259
+ async hydrate() {
260
+ this.status = "hydrating";
261
+ this.hydrationPromise = (async () => {
262
+ try {
263
+ if (!this.store) {
264
+ this.kernel.initialiseInMemory();
265
+ }
266
+ else {
267
+ const snapshotRec = await this.store.getLatestSnapshot(this.id);
268
+ let baseState;
269
+ let baseVersion;
270
+ let schemaVersion;
271
+ if (snapshotRec) {
272
+ const snapshot = deserialize(snapshotRec.data);
273
+ if (snapshot.entityDefName !== this.def[_name])
274
+ throw new DefinitionMismatchError(`Hydrating entity '${this.id}' with definition '${this.def[_name]}', but snapshot is from '${snapshot.entityDefName}'.`);
275
+ baseState = snapshot.state;
276
+ baseVersion = snapshotRec.version;
277
+ schemaVersion = snapshot.schemaVersion;
278
+ }
279
+ else {
280
+ baseState = this.def[_initialStateFn]();
281
+ baseVersion = 0n;
282
+ schemaVersion = 1;
283
+ }
284
+ const eventRecs = await this.store.getEvents(this.id, baseVersion);
285
+ const events = [];
286
+ let expectedVersion = baseVersion + 1n;
287
+ for (const rec of eventRecs) {
288
+ if (rec.version !== expectedVersion)
289
+ throw new OutOfOrderEventsError(`Hydrating entity '${this.id}': expected event version ${expectedVersion}, but got ${rec.version}.`);
290
+ const envelope = deserialize(rec.data);
291
+ events.push({
292
+ schemaVersion: envelope.schemaVersion,
293
+ patches: envelope.patches,
294
+ });
295
+ expectedVersion++;
296
+ }
297
+ this.kernel.hydrate({
298
+ baseState,
299
+ baseVersion,
300
+ schemaVersion,
301
+ events,
302
+ });
303
+ }
304
+ this.status = "active";
305
+ this.metrics?.onHydrate?.(this.id);
306
+ }
307
+ catch (err) {
308
+ this.status = "failed";
309
+ this.error = new HydrationError(`Failed to hydrate entity '${this.id}'`, { cause: err });
310
+ this.metrics?.onError?.(this.id, this.error);
311
+ throw this.error;
312
+ }
313
+ })();
314
+ await this.hydrationPromise;
315
+ }
316
+ async maybeSnapshot(force = false) {
317
+ const config = this.def[_persistence];
318
+ if (!this.store || !config?.snapshotEvery)
319
+ return;
320
+ if (force || this.kernel.version % BigInt(config.snapshotEvery) === 0n) {
321
+ try {
322
+ this.metrics?.onBeforeSnapshot?.(this.id, this.kernel.version);
323
+ const envelope = {
324
+ entityDefName: this.def[_name],
325
+ schemaVersion: this.def[_upcasters].length + 1,
326
+ state: this.kernel.state,
327
+ };
328
+ await this.store.commitSnapshot(this.id, this.kernel.version, serialize(envelope));
329
+ this.metrics?.onSnapshot?.(this.id, this.kernel.version);
330
+ }
331
+ catch (err) {
332
+ this.metrics?.onError?.(this.id, err instanceof Error ? err : new Error(String(err)));
333
+ }
334
+ }
335
+ }
336
+ onReset = async () => {
337
+ try {
338
+ await this.store?.clearEntity?.(this.id);
339
+ }
340
+ finally {
341
+ this.kernel.initialiseInMemory();
342
+ this.status = "active";
343
+ }
344
+ };
345
+ onStop = () => {
346
+ this.status = "failed";
347
+ this.error = new Error("Entity stopped by supervisor");
348
+ throw new StoppedEntityError(this.id);
349
+ };
350
+ buildContext() {
351
+ return {
352
+ self: { id: this.id, isFailed: this.isFailed },
353
+ clock: this.clock,
354
+ meta: { managerId: this.managerId, defName: this.def[_name] },
355
+ };
356
+ }
357
+ }
@@ -0,0 +1,15 @@
1
+ import type { AnyEntityDefinition, EntityManagerConfig, StateOf } from "../types/public";
2
+ import { Entity } from "./Entity";
3
+ export declare class RuntimeEntityManager<TDef extends AnyEntityDefinition> {
4
+ private config;
5
+ private entities;
6
+ private managerId;
7
+ private passivation?;
8
+ isShutdown: boolean;
9
+ constructor(config: EntityManagerConfig<TDef>);
10
+ getEntity(id: string): Entity<StateOf<TDef>>;
11
+ removeEntity(id: string): void;
12
+ private evictEntity;
13
+ terminate(): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=EntityManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityManager.d.ts","sourceRoot":"","sources":["../../src/runtime/EntityManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,OAAO,EACR,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,qBAAa,oBAAoB,CAAC,IAAI,SAAS,mBAAmB;IAMpD,OAAO,CAAC,MAAM;IAL1B,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAqB;IAClC,UAAU,UAAS;gBAEN,MAAM,EAAE,mBAAmB,CAAC,IAAI,CAAC;IAYrD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAqB5C,YAAY,CAAC,EAAE,EAAE,MAAM;IAIvB,OAAO,CAAC,WAAW,CAQjB;IAEI,SAAS;CAQhB"}
@@ -0,0 +1,46 @@
1
+ import { ManagerShutdownError } from "../errors";
2
+ import { newId } from "../utils/id";
3
+ import { Entity } from "./Entity";
4
+ import { PassivationManager } from "./Passivation";
5
+ export class RuntimeEntityManager {
6
+ config;
7
+ entities = new Map();
8
+ managerId = newId();
9
+ passivation;
10
+ isShutdown = false;
11
+ constructor(config) {
12
+ this.config = config;
13
+ if (config.passivation) {
14
+ this.passivation = new PassivationManager(this.entities, this.evictEntity, config.passivation.idleAfter, config.passivation.sweepInterval ?? 60_000, () => Date.now());
15
+ }
16
+ }
17
+ getEntity(id) {
18
+ if (this.isShutdown) {
19
+ throw new ManagerShutdownError("EntityManager is shut down. Cannot create new entities.");
20
+ }
21
+ let entity = this.entities.get(id);
22
+ if (!entity || entity.isFailed) {
23
+ entity = new Entity(id, this.config.definition, this.managerId, this.config.store, this.config.supervisor, this.config.metrics);
24
+ this.entities.set(id, entity);
25
+ }
26
+ return entity;
27
+ }
28
+ removeEntity(id) {
29
+ this.entities.delete(id);
30
+ }
31
+ evictEntity = async (id) => {
32
+ const entity = this.entities.get(id);
33
+ if (entity) {
34
+ await entity.maybeSnapshot(true);
35
+ await entity.terminate();
36
+ this.entities.delete(id);
37
+ this.config.metrics?.onEvict?.(id);
38
+ }
39
+ };
40
+ async terminate() {
41
+ this.isShutdown = true;
42
+ this.passivation?.stop();
43
+ await Promise.allSettled([...this.entities.values()].map((e) => e.terminate()));
44
+ this.entities.clear();
45
+ }
46
+ }
@@ -0,0 +1,5 @@
1
+ export declare class Mailbox {
2
+ private queue;
3
+ enqueue<T>(task: () => Promise<T>): Promise<T>;
4
+ }
5
+ //# sourceMappingURL=Mailbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Mailbox.d.ts","sourceRoot":"","sources":["../../src/runtime/Mailbox.ts"],"names":[],"mappings":"AAAA,qBAAa,OAAO;IAClB,OAAO,CAAC,KAAK,CAAuC;IAEpD,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAK/C"}
@@ -0,0 +1,8 @@
1
+ export class Mailbox {
2
+ queue = Promise.resolve();
3
+ enqueue(task) {
4
+ const taskPromise = this.queue.then(task);
5
+ this.queue = taskPromise.catch(() => { }); // prevent unhandled promise rejection
6
+ return taskPromise;
7
+ }
8
+ }
@@ -0,0 +1,12 @@
1
+ import type { Entity } from "./Entity";
2
+ export declare class PassivationManager {
3
+ private entities;
4
+ private onEvict;
5
+ private idleAfterMs;
6
+ private now;
7
+ private sweeper?;
8
+ constructor(entities: Map<string, Entity<any>>, onEvict: (id: string) => Promise<void> | void, idleAfterMs: number, sweepIntervalMs: number, now?: () => number);
9
+ private sweep;
10
+ stop(): void;
11
+ }
12
+ //# sourceMappingURL=Passivation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Passivation.d.ts","sourceRoot":"","sources":["../../src/runtime/Passivation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,WAAW;IAEnB,OAAO,CAAC,GAAG;IARb,OAAO,CAAC,OAAO,CAAC,CAA6C;gBAInD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAClC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAC7C,WAAW,EAAE,MAAM,EAC3B,eAAe,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,MAAyB;YAYhC,KAAK;IAanB,IAAI;CAML"}
@@ -0,0 +1,42 @@
1
+ export class PassivationManager {
2
+ entities;
3
+ onEvict;
4
+ idleAfterMs;
5
+ now;
6
+ sweeper;
7
+ constructor(
8
+ // biome-ignore lint/suspicious/noExplicitAny: Entity can have any state
9
+ entities, onEvict, idleAfterMs, sweepIntervalMs, now = () => Date.now()) {
10
+ this.entities = entities;
11
+ this.onEvict = onEvict;
12
+ this.idleAfterMs = idleAfterMs;
13
+ this.now = now;
14
+ this.sweeper = setInterval(() => {
15
+ // Run the sweep as an async task; swallow errors but complete its awaits.
16
+ (async () => {
17
+ await this.sweep();
18
+ })().catch(() => { });
19
+ }, sweepIntervalMs);
20
+ // `unref` doesn't exist in some environments (e.g., browsers)
21
+ this.sweeper.unref?.();
22
+ }
23
+ async sweep() {
24
+ const now = this.now();
25
+ const pending = [];
26
+ for (const [id, entity] of this.entities.entries()) {
27
+ if (entity.isFailed)
28
+ continue;
29
+ if (now - entity.lastActivity > this.idleAfterMs) {
30
+ pending.push(this.onEvict(id));
31
+ }
32
+ }
33
+ // Ensure evictions complete (important for fake-timer tests)
34
+ await Promise.allSettled(pending);
35
+ }
36
+ stop() {
37
+ if (this.sweeper) {
38
+ clearInterval(this.sweeper);
39
+ this.sweeper = undefined;
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,4 @@
1
+ import type { Supervisor } from "../types/public";
2
+ export declare function Supervise<T>(task: () => Promise<T>, state: unknown, supervisor: Supervisor, onReset: () => Promise<void>, // side-effect; we will throw here
3
+ onStop: () => never): Promise<T>;
4
+ //# sourceMappingURL=Supervision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Supervision.d.ts","sourceRoot":"","sources":["../../src/runtime/Supervision.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,wBAAsB,SAAS,CAAC,CAAC,EAC/B,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,KAAK,EAAE,OAAO,EACd,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,kCAAkC;AAChE,MAAM,EAAE,MAAM,KAAK,GAClB,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
@@ -0,0 +1,20 @@
1
+ import { ResetError } from "../errors";
2
+ export async function Supervise(task, state, supervisor, onReset, // side-effect; we will throw here
3
+ onStop) {
4
+ try {
5
+ return await task();
6
+ }
7
+ catch (err) {
8
+ const error = err instanceof Error ? err : new Error(String(err));
9
+ const strategy = supervisor.strategy(state, error);
10
+ switch (strategy) {
11
+ case "resume":
12
+ throw error;
13
+ case "reset":
14
+ await onReset();
15
+ throw new ResetError(error);
16
+ case "stop":
17
+ onStop(); // this should throw
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,7 @@
1
+ export declare const clone: <T>(value: T) => T;
2
+ export declare const serialize: (value: unknown) => string;
3
+ export declare const deserialize: <T = unknown>(value: string) => T;
4
+ export declare const serializeComparable: (value: unknown) => unknown;
5
+ export declare const deepEqual: (a: unknown, b: unknown) => boolean;
6
+ export declare const escapeJsonPointerToken: (token: string | number) => string;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/serde/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,CACc,CAAC;AAEnD,eAAO,MAAM,SAAS,GAAI,OAAO,OAAO,KAAG,MAAoC,CAAC;AAEhF,eAAO,MAAM,WAAW,GAAI,CAAC,GAAG,OAAO,EAAE,OAAO,MAAM,KAAG,CACjC,CAAC;AAEzB,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,OACpB,CAAC;AAElC,eAAO,MAAM,SAAS,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,KAAG,OAQlD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,GAAG,MAAM,KAAG,MAG/D,CAAC"}
@@ -0,0 +1,19 @@
1
+ import superjson from "superjson";
2
+ export const clone = (value) => superjson.parse(superjson.stringify(value));
3
+ export const serialize = (value) => superjson.stringify(value);
4
+ export const deserialize = (value) => superjson.parse(value);
5
+ export const serializeComparable = (value) => superjson.serialize(value).json;
6
+ export const deepEqual = (a, b) => {
7
+ try {
8
+ const serializedA = superjson.stringify(a);
9
+ const serializedB = superjson.stringify(b);
10
+ return serializedA === serializedB;
11
+ }
12
+ catch {
13
+ return a === b;
14
+ }
15
+ };
16
+ export const escapeJsonPointerToken = (token) => {
17
+ const str = String(token);
18
+ return str.replace(/~/g, "~0").replace(/\//g, "~1");
19
+ };
@@ -0,0 +1,10 @@
1
+ export declare const _name: unique symbol;
2
+ export declare const _state: unique symbol;
3
+ export declare const _messages: unique symbol;
4
+ export declare const _tag: unique symbol;
5
+ export declare const _initialStateFn: unique symbol;
6
+ export declare const _upcasters: unique symbol;
7
+ export declare const _handlers: unique symbol;
8
+ export declare const _persistence: unique symbol;
9
+ export declare const _versions: unique symbol;
10
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../src/types/internal.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,KAAK,eAAyB,CAAC;AAC5C,eAAO,MAAM,MAAM,eAA0B,CAAC;AAC9C,eAAO,MAAM,SAAS,eAA6B,CAAC;AACpD,eAAO,MAAM,IAAI,eAAwB,CAAC;AAC1C,eAAO,MAAM,eAAe,eAAmC,CAAC;AAChE,eAAO,MAAM,UAAU,eAA8B,CAAC;AACtD,eAAO,MAAM,SAAS,eAA6B,CAAC;AACpD,eAAO,MAAM,YAAY,eAAgC,CAAC;AAC1D,eAAO,MAAM,SAAS,eAA6B,CAAC"}
@@ -0,0 +1,10 @@
1
+ // Branded symbols to hide internal properties from the public API and prevent accidental access.
2
+ export const _name = Symbol.for("cue_name");
3
+ export const _state = Symbol.for("cue_state");
4
+ export const _messages = Symbol.for("cue_messages");
5
+ export const _tag = Symbol.for("cue_tag");
6
+ export const _initialStateFn = Symbol.for("cue_initialStateFn");
7
+ export const _upcasters = Symbol.for("cue_upcasters");
8
+ export const _handlers = Symbol.for("cue_handlers");
9
+ export const _persistence = Symbol.for("cue_persistence");
10
+ export const _versions = Symbol.for("cue_versions");