@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.
- package/LICENSE +21 -0
- package/README.md +410 -0
- package/dist/api/create.d.ts +3 -0
- package/dist/api/create.d.ts.map +1 -0
- package/dist/api/create.js +106 -0
- package/dist/api/define.d.ts +40 -0
- package/dist/api/define.d.ts.map +1 -0
- package/dist/api/define.js +61 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +7 -0
- package/dist/api/supervisor.d.ts +22 -0
- package/dist/api/supervisor.d.ts.map +1 -0
- package/dist/api/supervisor.js +39 -0
- package/dist/core/Evolution.d.ts +11 -0
- package/dist/core/Evolution.d.ts.map +1 -0
- package/dist/core/Evolution.js +29 -0
- package/dist/core/StateKernel.d.ts +35 -0
- package/dist/core/StateKernel.d.ts.map +1 -0
- package/dist/core/StateKernel.js +113 -0
- package/dist/errors/index.d.ts +22 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +43 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/persistence/adapters/inMemory.d.ts +21 -0
- package/dist/persistence/adapters/inMemory.d.ts.map +1 -0
- package/dist/persistence/adapters/inMemory.js +41 -0
- package/dist/persistence/types.d.ts +28 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +1 -0
- package/dist/runtime/Entity.d.ts +42 -0
- package/dist/runtime/Entity.d.ts.map +1 -0
- package/dist/runtime/Entity.js +357 -0
- package/dist/runtime/EntityManager.d.ts +15 -0
- package/dist/runtime/EntityManager.d.ts.map +1 -0
- package/dist/runtime/EntityManager.js +46 -0
- package/dist/runtime/Mailbox.d.ts +5 -0
- package/dist/runtime/Mailbox.d.ts.map +1 -0
- package/dist/runtime/Mailbox.js +8 -0
- package/dist/runtime/Passivation.d.ts +12 -0
- package/dist/runtime/Passivation.d.ts.map +1 -0
- package/dist/runtime/Passivation.js +42 -0
- package/dist/runtime/Supervision.d.ts +4 -0
- package/dist/runtime/Supervision.d.ts.map +1 -0
- package/dist/runtime/Supervision.js +20 -0
- package/dist/serde/index.d.ts +7 -0
- package/dist/serde/index.d.ts.map +1 -0
- package/dist/serde/index.js +19 -0
- package/dist/types/internal.d.ts +10 -0
- package/dist/types/internal.d.ts.map +1 -0
- package/dist/types/internal.js +10 -0
- package/dist/types/public.d.ts +168 -0
- package/dist/types/public.d.ts.map +1 -0
- package/dist/types/public.js +1 -0
- package/dist/utils/clock.d.ts +5 -0
- package/dist/utils/clock.d.ts.map +1 -0
- package/dist/utils/clock.js +3 -0
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +2 -0
- package/dist/utils/invariants.d.ts +2 -0
- package/dist/utils/invariants.d.ts.map +1 -0
- package/dist/utils/invariants.js +5 -0
- package/package.json +71 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Supervisor, SupervisorStrategy } from "../types/public";
|
|
2
|
+
type Guard = (state: unknown, error: Error) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Ergonomic builder for a Supervisor without stringly-typed returns.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const sup = supervisor({
|
|
8
|
+
* // Choose a strategy when the guard returns true.
|
|
9
|
+
* resume: (state, err) => err.name === "ValidationError",
|
|
10
|
+
* stop: (state, err) => err.name === "CatastrophicConfigError",
|
|
11
|
+
* // Fallback if no guard matches (defaults to "resume")
|
|
12
|
+
* default: "reset",
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
export declare function supervisor(spec: {
|
|
16
|
+
resume?: boolean | Guard;
|
|
17
|
+
reset?: boolean | Guard;
|
|
18
|
+
stop?: boolean | Guard;
|
|
19
|
+
default?: SupervisorStrategy;
|
|
20
|
+
}): Supervisor;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=supervisor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/api/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,MAAM,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B,GAAG,UAAU,CAuBb"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ergonomic builder for a Supervisor without stringly-typed returns.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* const sup = supervisor({
|
|
6
|
+
* // Choose a strategy when the guard returns true.
|
|
7
|
+
* resume: (state, err) => err.name === "ValidationError",
|
|
8
|
+
* stop: (state, err) => err.name === "CatastrophicConfigError",
|
|
9
|
+
* // Fallback if no guard matches (defaults to "resume")
|
|
10
|
+
* default: "reset",
|
|
11
|
+
* });
|
|
12
|
+
*/
|
|
13
|
+
export function supervisor(spec) {
|
|
14
|
+
const toGuard = (g) => {
|
|
15
|
+
if (g === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (typeof g === "function")
|
|
18
|
+
return g;
|
|
19
|
+
return () => g; // boolean -> constant guard
|
|
20
|
+
};
|
|
21
|
+
const guards = {
|
|
22
|
+
// Strategy precedence: stop → reset → resume
|
|
23
|
+
stop: toGuard(spec.stop),
|
|
24
|
+
reset: toGuard(spec.reset),
|
|
25
|
+
resume: toGuard(spec.resume),
|
|
26
|
+
};
|
|
27
|
+
const fallback = spec.default ?? "resume";
|
|
28
|
+
return {
|
|
29
|
+
strategy(state, error) {
|
|
30
|
+
if (guards.stop?.(state, error))
|
|
31
|
+
return "stop";
|
|
32
|
+
if (guards.reset?.(state, error))
|
|
33
|
+
return "reset";
|
|
34
|
+
if (guards.resume?.(state, error))
|
|
35
|
+
return "resume";
|
|
36
|
+
return fallback;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnyEntityDefinition } from "../types/public";
|
|
2
|
+
declare function applyUpcasters<T = any>(state: T, fromSchemaVersion: number, def: AnyEntityDefinition): T;
|
|
3
|
+
declare function applyUpcastersTo<T = any>(state: T, fromSchemaVersion: number, toSchemaVersion: number, def: AnyEntityDefinition): T;
|
|
4
|
+
declare function getLatestInitialState(def: AnyEntityDefinition): any;
|
|
5
|
+
export declare const Evolution: {
|
|
6
|
+
applyUpcasters: typeof applyUpcasters;
|
|
7
|
+
applyUpcastersTo: typeof applyUpcastersTo;
|
|
8
|
+
getLatestInitialState: typeof getLatestInitialState;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=Evolution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Evolution.d.ts","sourceRoot":"","sources":["../../src/core/Evolution.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,iBAAS,cAAc,CAAC,CAAC,GAAG,GAAG,EAC7B,KAAK,EAAE,CAAC,EACR,iBAAiB,EAAE,MAAM,EACzB,GAAG,EAAE,mBAAmB,GACvB,CAAC,CAOH;AAGD,iBAAS,gBAAgB,CAAC,CAAC,GAAG,GAAG,EAC/B,KAAK,EAAE,CAAC,EACR,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,GAAG,EAAE,mBAAmB,GACvB,CAAC,CAUH;AAGD,iBAAS,qBAAqB,CAAC,GAAG,EAAE,mBAAmB,GAAG,GAAG,CAG5D;AAED,eAAO,MAAM,SAAS;;;;CAIrB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { _initialStateFn, _upcasters } from "../types/internal";
|
|
2
|
+
// biome-ignore lint/suspicious/noExplicitAny: patches can apply to any state shape
|
|
3
|
+
function applyUpcasters(state, fromSchemaVersion, def) {
|
|
4
|
+
const upcastersToRun = def[_upcasters].slice(fromSchemaVersion - 1);
|
|
5
|
+
let evolvedState = state;
|
|
6
|
+
for (const upcaster of upcastersToRun) {
|
|
7
|
+
evolvedState = upcaster(evolvedState);
|
|
8
|
+
}
|
|
9
|
+
return evolvedState;
|
|
10
|
+
}
|
|
11
|
+
// biome-ignore lint/suspicious/noExplicitAny: state shapes vary across versions
|
|
12
|
+
function applyUpcastersTo(state, fromSchemaVersion, toSchemaVersion, def) {
|
|
13
|
+
const upcastersToRun = def[_upcasters].slice(fromSchemaVersion - 1, toSchemaVersion - 1);
|
|
14
|
+
let evolvedState = state;
|
|
15
|
+
for (const upcaster of upcastersToRun) {
|
|
16
|
+
evolvedState = upcaster(evolvedState);
|
|
17
|
+
}
|
|
18
|
+
return evolvedState;
|
|
19
|
+
}
|
|
20
|
+
// biome-ignore lint/suspicious/noExplicitAny: initial state can be any shape
|
|
21
|
+
function getLatestInitialState(def) {
|
|
22
|
+
const initialState = def[_initialStateFn]();
|
|
23
|
+
return applyUpcasters(initialState, 1, def);
|
|
24
|
+
}
|
|
25
|
+
export const Evolution = {
|
|
26
|
+
applyUpcasters,
|
|
27
|
+
applyUpcastersTo,
|
|
28
|
+
getLatestInitialState,
|
|
29
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AnyEntityDefinition, HandlerContext, Patch } from "../types/public";
|
|
2
|
+
export declare class StateKernel<TState extends object> {
|
|
3
|
+
#private;
|
|
4
|
+
private readonly def;
|
|
5
|
+
constructor(def: AnyEntityDefinition);
|
|
6
|
+
get state(): TState;
|
|
7
|
+
get version(): bigint;
|
|
8
|
+
initialiseInMemory(): void;
|
|
9
|
+
hydrate(params: {
|
|
10
|
+
baseState: TState;
|
|
11
|
+
baseVersion: bigint;
|
|
12
|
+
schemaVersion: number;
|
|
13
|
+
events: Array<{
|
|
14
|
+
schemaVersion: number;
|
|
15
|
+
patches: Patch;
|
|
16
|
+
}>;
|
|
17
|
+
}): void;
|
|
18
|
+
getLatestInitialState(): TState;
|
|
19
|
+
applyCommittedState(nextState: TState): void;
|
|
20
|
+
applyCommand(handlerName: string, args: unknown[], ctx: HandlerContext): Promise<{
|
|
21
|
+
returnValue: unknown;
|
|
22
|
+
patches: Patch;
|
|
23
|
+
nextState: TState;
|
|
24
|
+
}>;
|
|
25
|
+
runQuery(handlerName: string, args: unknown[], ctx: HandlerContext): unknown;
|
|
26
|
+
startStream(handlerName: string, args: unknown[], ctx: HandlerContext): {
|
|
27
|
+
generator: AsyncGenerator;
|
|
28
|
+
finalize: () => {
|
|
29
|
+
patches: Patch;
|
|
30
|
+
nextState: TState;
|
|
31
|
+
};
|
|
32
|
+
discard: () => void;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=StateKernel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateKernel.d.ts","sourceRoot":"","sources":["../../src/core/StateKernel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,KAAK,EACN,MAAM,iBAAiB,CAAC;AAIzB,qBAAa,WAAW,CAAC,MAAM,SAAS,MAAM;;IAIhC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,mBAAmB;IAErD,IAAI,KAAK,IAAI,MAAM,CAMlB;IAED,IAAI,OAAO,IAAI,MAAM,CAMpB;IAED,kBAAkB;IAKlB,OAAO,CAAC,MAAM,EAAE;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,KAAK,CAAC;YAAE,aAAa,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,KAAK,CAAA;SAAE,CAAC,CAAC;KAC1D;IAsBD,qBAAqB,IAAI,MAAM;IAI/B,mBAAmB,CAAC,SAAS,EAAE,MAAM;IAK/B,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,OAAO,EAAE,EACf,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,KAAK,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAsBvE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO;IAe5E,WAAW,CACT,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,OAAO,EAAE,EACf,GAAG,EAAE,cAAc,GAClB;QACD,SAAS,EAAE,cAAc,CAAC;QAC1B,QAAQ,EAAE,MAAM;YAAE,OAAO,EAAE,KAAK,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,OAAO,EAAE,MAAM,IAAI,CAAC;KACrB;CAiDF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { applyPatches, createDraft, finishDraft, isDraft, } from "immer";
|
|
2
|
+
import { _handlers } from "../types/internal";
|
|
3
|
+
import { invariant } from "../utils/invariants";
|
|
4
|
+
import { Evolution } from "./Evolution";
|
|
5
|
+
export class StateKernel {
|
|
6
|
+
def;
|
|
7
|
+
#state = null;
|
|
8
|
+
#version = -1n;
|
|
9
|
+
constructor(def) {
|
|
10
|
+
this.def = def;
|
|
11
|
+
}
|
|
12
|
+
get state() {
|
|
13
|
+
invariant(this.#state, "State not available. Ensure the entity is hydrated.");
|
|
14
|
+
return this.#state;
|
|
15
|
+
}
|
|
16
|
+
get version() {
|
|
17
|
+
invariant(this.#version >= 0n, "Version not available. Ensure the entity is hydrated.");
|
|
18
|
+
return this.#version;
|
|
19
|
+
}
|
|
20
|
+
initialiseInMemory() {
|
|
21
|
+
this.#state = Evolution.getLatestInitialState(this.def);
|
|
22
|
+
this.#version = 0n;
|
|
23
|
+
}
|
|
24
|
+
hydrate(params) {
|
|
25
|
+
let state = params.baseState;
|
|
26
|
+
let currentSchema = params.schemaVersion;
|
|
27
|
+
for (const event of params.events) {
|
|
28
|
+
if (event.schemaVersion > currentSchema) {
|
|
29
|
+
state = Evolution.applyUpcastersTo(state, currentSchema, event.schemaVersion, this.def);
|
|
30
|
+
currentSchema = event.schemaVersion;
|
|
31
|
+
}
|
|
32
|
+
state = applyPatches(state, event.patches);
|
|
33
|
+
}
|
|
34
|
+
// Upcast to latest schema version
|
|
35
|
+
this.#state = Evolution.applyUpcasters(state, currentSchema, this.def);
|
|
36
|
+
this.#version = params.baseVersion + BigInt(params.events.length);
|
|
37
|
+
}
|
|
38
|
+
getLatestInitialState() {
|
|
39
|
+
return Evolution.getLatestInitialState(this.def);
|
|
40
|
+
}
|
|
41
|
+
applyCommittedState(nextState) {
|
|
42
|
+
this.#state = nextState;
|
|
43
|
+
this.#version += 1n;
|
|
44
|
+
}
|
|
45
|
+
async applyCommand(handlerName, args, ctx) {
|
|
46
|
+
const entry = this.def[_handlers][handlerName];
|
|
47
|
+
invariant(entry && entry.type === "command", `Handler "${handlerName}" not found or not a command.`);
|
|
48
|
+
let patches = [];
|
|
49
|
+
const draft = createDraft(this.state);
|
|
50
|
+
try {
|
|
51
|
+
const returnValue = await Promise.resolve(entry.fn(draft, ...args, ctx));
|
|
52
|
+
const nextState = finishDraft(draft, (p) => {
|
|
53
|
+
patches = p;
|
|
54
|
+
});
|
|
55
|
+
return { returnValue, patches, nextState };
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
finishDraft(draft);
|
|
59
|
+
throw e;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
runQuery(handlerName, args, ctx) {
|
|
63
|
+
const entry = this.def[_handlers][handlerName];
|
|
64
|
+
invariant(entry && entry.type === "query", `Handler "${handlerName}" not found or not a query.`);
|
|
65
|
+
const draft = createDraft(this.state);
|
|
66
|
+
try {
|
|
67
|
+
return entry.fn(draft, ...args, ctx);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
finishDraft(draft); // discards changes
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
startStream(handlerName, args, ctx) {
|
|
74
|
+
const entry = this.def[_handlers][handlerName];
|
|
75
|
+
invariant(entry && entry.type === "stream", `Handler "${handlerName}" not found or not a stream.`);
|
|
76
|
+
const draft = createDraft(this.state);
|
|
77
|
+
const generatorImpl = entry.fn(draft, ...args, ctx);
|
|
78
|
+
invariant(generatorImpl && typeof generatorImpl.next === "function", `Stream handler "${handlerName}" did not return an AsyncGenerator.`);
|
|
79
|
+
const generator = {
|
|
80
|
+
async next(...a) {
|
|
81
|
+
return await generatorImpl.next(...a);
|
|
82
|
+
},
|
|
83
|
+
async return(...a) {
|
|
84
|
+
return generatorImpl.return(...a);
|
|
85
|
+
},
|
|
86
|
+
async throw(...a) {
|
|
87
|
+
return generatorImpl.throw(...a);
|
|
88
|
+
},
|
|
89
|
+
[Symbol.asyncIterator]() {
|
|
90
|
+
return this;
|
|
91
|
+
},
|
|
92
|
+
// TS 5.9 AsyncDisposable – no-op to satisfy the interface
|
|
93
|
+
async [Symbol.asyncDispose]() {
|
|
94
|
+
/* noop */
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
generator,
|
|
99
|
+
finalize: () => {
|
|
100
|
+
let patches = [];
|
|
101
|
+
const nextState = finishDraft(draft, (p) => {
|
|
102
|
+
patches = p;
|
|
103
|
+
});
|
|
104
|
+
return { patches, nextState };
|
|
105
|
+
},
|
|
106
|
+
discard: () => {
|
|
107
|
+
if (isDraft(draft)) {
|
|
108
|
+
finishDraft(draft);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare class ResetError extends Error {
|
|
2
|
+
constructor(original: Error);
|
|
3
|
+
}
|
|
4
|
+
export declare class DefinitionMismatchError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class OutOfOrderEventsError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class CommitError extends Error {
|
|
11
|
+
constructor(message: string, cause?: unknown);
|
|
12
|
+
}
|
|
13
|
+
export declare class HydrationError extends Error {
|
|
14
|
+
constructor(message: string, cause?: unknown);
|
|
15
|
+
}
|
|
16
|
+
export declare class StoppedEntityError extends Error {
|
|
17
|
+
constructor(entityId: string);
|
|
18
|
+
}
|
|
19
|
+
export declare class ManagerShutdownError extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;gBACvB,QAAQ,EAAE,KAAK;CAK5B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAI7C;AAED,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAI7C;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,QAAQ,EAAE,MAAM;CAM7B;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class ResetError extends Error {
|
|
2
|
+
constructor(original) {
|
|
3
|
+
super(`Entity reset after error: ${original.message}`);
|
|
4
|
+
this.cause = original;
|
|
5
|
+
this.name = "ResetError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class DefinitionMismatchError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "DefinitionMismatchError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class OutOfOrderEventsError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "OutOfOrderEventsError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class CommitError extends Error {
|
|
21
|
+
constructor(message, cause) {
|
|
22
|
+
super(message, { cause });
|
|
23
|
+
this.name = "CommitError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class HydrationError extends Error {
|
|
27
|
+
constructor(message, cause) {
|
|
28
|
+
super(message, { cause });
|
|
29
|
+
this.name = "HydrationError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class StoppedEntityError extends Error {
|
|
33
|
+
constructor(entityId) {
|
|
34
|
+
super(`Entity ${entityId} is stopped/failed. Further messages are rejected.`);
|
|
35
|
+
this.name = "StoppedEntityError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class ManagerShutdownError extends Error {
|
|
39
|
+
constructor(message) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "ManagerShutdownError";
|
|
42
|
+
}
|
|
43
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,mBAAmB,qBAAqB,CAAC;AACzC,mBAAmB,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PersistenceAdapter } from "../types";
|
|
2
|
+
interface EventRecord {
|
|
3
|
+
version: bigint;
|
|
4
|
+
data: string;
|
|
5
|
+
}
|
|
6
|
+
interface SnapshotRecord {
|
|
7
|
+
version: bigint;
|
|
8
|
+
data: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class InMemoryPersistenceAdapter implements PersistenceAdapter {
|
|
11
|
+
private events;
|
|
12
|
+
private snapshots;
|
|
13
|
+
getEvents(entityId: string, fromVersion: bigint): Promise<EventRecord[]>;
|
|
14
|
+
commitEvent(entityId: string, version: bigint, data: string): Promise<void>;
|
|
15
|
+
getLatestSnapshot(entityId: string): Promise<SnapshotRecord | null>;
|
|
16
|
+
commitSnapshot(entityId: string, version: bigint, data: string): Promise<void>;
|
|
17
|
+
clearEntity(entityId: string): Promise<void>;
|
|
18
|
+
clear(): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=inMemory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../../src/persistence/adapters/inMemory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAWD,qBAAa,0BAA2B,YAAW,kBAAkB;IACnE,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,SAAS,CAAqC;IAEhD,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,EAAE,CAAC;IAKnB,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAcV,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAKnE,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAIV,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlD,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CommitError } from "../../errors/index";
|
|
2
|
+
import { clone } from "../../serde/index";
|
|
3
|
+
function auditVersion(currentVersion, newVersion) {
|
|
4
|
+
const expectedVersion = currentVersion + 1n;
|
|
5
|
+
if (newVersion !== expectedVersion) {
|
|
6
|
+
throw new CommitError(`Optimistic lock failure: expected version ${expectedVersion}, got ${newVersion}.`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class InMemoryPersistenceAdapter {
|
|
10
|
+
events = new Map();
|
|
11
|
+
snapshots = new Map();
|
|
12
|
+
async getEvents(entityId, fromVersion) {
|
|
13
|
+
const entityEvents = this.events.get(entityId) ?? [];
|
|
14
|
+
return clone(entityEvents.filter((event) => event.version > fromVersion));
|
|
15
|
+
}
|
|
16
|
+
async commitEvent(entityId, version, data) {
|
|
17
|
+
const entityEvents = this.events.get(entityId) ?? [];
|
|
18
|
+
const lastEventVersion = entityEvents.at(-1)?.version ?? 0n;
|
|
19
|
+
const snapshotVersion = this.snapshots.get(entityId)?.version ?? 0n;
|
|
20
|
+
const currentVersion = lastEventVersion > snapshotVersion ? lastEventVersion : snapshotVersion;
|
|
21
|
+
auditVersion(currentVersion, version);
|
|
22
|
+
entityEvents.push({ version, data });
|
|
23
|
+
this.events.set(entityId, entityEvents);
|
|
24
|
+
}
|
|
25
|
+
async getLatestSnapshot(entityId) {
|
|
26
|
+
const snapshot = this.snapshots.get(entityId);
|
|
27
|
+
return snapshot ? clone(snapshot) : null;
|
|
28
|
+
}
|
|
29
|
+
async commitSnapshot(entityId, version, data) {
|
|
30
|
+
this.snapshots.set(entityId, { version, data });
|
|
31
|
+
}
|
|
32
|
+
async clearEntity(entityId) {
|
|
33
|
+
this.events.delete(entityId);
|
|
34
|
+
this.snapshots.delete(entityId);
|
|
35
|
+
}
|
|
36
|
+
// test helper
|
|
37
|
+
clear() {
|
|
38
|
+
this.events.clear();
|
|
39
|
+
this.snapshots.clear();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Patch } from "../types/public";
|
|
2
|
+
export type EventEnvelope = {
|
|
3
|
+
entityDefName: string;
|
|
4
|
+
schemaVersion: number;
|
|
5
|
+
handler: string;
|
|
6
|
+
payload: unknown[];
|
|
7
|
+
returnVal?: unknown;
|
|
8
|
+
patches: Patch;
|
|
9
|
+
};
|
|
10
|
+
export type SnapshotEnvelope = {
|
|
11
|
+
entityDefName: string;
|
|
12
|
+
schemaVersion: number;
|
|
13
|
+
state: unknown;
|
|
14
|
+
};
|
|
15
|
+
export interface PersistenceAdapter {
|
|
16
|
+
getEvents(entityId: string, fromVersion: bigint): Promise<{
|
|
17
|
+
version: bigint;
|
|
18
|
+
data: string;
|
|
19
|
+
}[]>;
|
|
20
|
+
commitEvent(entityId: string, version: bigint, data: string): Promise<void>;
|
|
21
|
+
getLatestSnapshot(entityId: string): Promise<{
|
|
22
|
+
version: bigint;
|
|
23
|
+
data: string;
|
|
24
|
+
} | null>;
|
|
25
|
+
commitSnapshot(entityId: string, version: bigint, data: string): Promise<void>;
|
|
26
|
+
clearEntity?(entityId: string): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/persistence/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,SAAS,CACP,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IAEhD,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,iBAAiB,CACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAErD,cAAc,CACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PersistenceAdapter } from "../persistence/types";
|
|
2
|
+
import type { AnyEntityDefinition, EntityMetrics, Supervisor } from "../types/public";
|
|
3
|
+
import type { Clock } from "../utils/clock";
|
|
4
|
+
export declare class Entity<TState extends object> {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
private readonly def;
|
|
7
|
+
private readonly managerId;
|
|
8
|
+
private readonly store?;
|
|
9
|
+
private readonly supervisor?;
|
|
10
|
+
private readonly metrics?;
|
|
11
|
+
private readonly clock;
|
|
12
|
+
private kernel;
|
|
13
|
+
private status;
|
|
14
|
+
private mailbox;
|
|
15
|
+
private lastTouch;
|
|
16
|
+
private error?;
|
|
17
|
+
private hydrationPromise?;
|
|
18
|
+
constructor(id: string, def: AnyEntityDefinition, managerId: string, store?: PersistenceAdapter | undefined, supervisor?: Supervisor | undefined, metrics?: EntityMetrics | undefined, clock?: Clock);
|
|
19
|
+
get isFailed(): boolean;
|
|
20
|
+
get isShutdown(): boolean;
|
|
21
|
+
get lastActivity(): number;
|
|
22
|
+
tell: (handlerName: string, args: unknown[]) => Promise<unknown>;
|
|
23
|
+
ask: (handlerName: string, args: unknown[]) => Promise<unknown>;
|
|
24
|
+
stream: (handlerName: string, args: unknown[]) => AsyncGenerator<unknown, unknown, unknown>;
|
|
25
|
+
inspect: () => Promise<{
|
|
26
|
+
state: TState;
|
|
27
|
+
version: bigint;
|
|
28
|
+
}>;
|
|
29
|
+
terminate: () => Promise<void>;
|
|
30
|
+
stateAt: (targetVersion: bigint) => Promise<{
|
|
31
|
+
schemaVersion: number;
|
|
32
|
+
state: unknown;
|
|
33
|
+
}>;
|
|
34
|
+
private commit;
|
|
35
|
+
private ensureActive;
|
|
36
|
+
private hydrate;
|
|
37
|
+
maybeSnapshot(force?: boolean): Promise<void>;
|
|
38
|
+
private onReset;
|
|
39
|
+
private onStop;
|
|
40
|
+
private buildContext;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=Entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Entity.d.ts","sourceRoot":"","sources":["../../src/runtime/Entity.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,kBAAkB,EAEnB,MAAM,sBAAsB,CAAC;AAS9B,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EAEb,UAAU,EACX,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAO5C,qBAAa,MAAM,CAAC,MAAM,SAAS,MAAM;aASrB,EAAE,EAAE,MAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAdxB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAC,CAAQ;IACtB,OAAO,CAAC,gBAAgB,CAAC,CAAgB;gBAGvB,EAAE,EAAE,MAAM,EACT,GAAG,EAAE,mBAAmB,EACxB,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,kBAAkB,YAAA,EAC1B,UAAU,CAAC,EAAE,UAAU,YAAA,EACvB,OAAO,CAAC,EAAE,aAAa,YAAA,EACvB,KAAK,GAAE,KAAiB;IAM3C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAEM,IAAI,GAAI,aAAa,MAAM,EAAE,MAAM,OAAO,EAAE,KAAG,OAAO,CAAC,OAAO,CAAC,CA6DpE;IAEK,GAAG,GAAI,aAAa,MAAM,EAAE,MAAM,OAAO,EAAE,KAAG,OAAO,CAAC,OAAO,CAAC,CAKnE;IAEK,MAAM,GACX,aAAa,MAAM,EACnB,MAAM,OAAO,EAAE,KACd,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAuG1C;IAEK,OAAO,QAAa,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAMpE;IAEK,SAAS,QAAa,OAAO,CAAC,IAAI,CAAC,CAKxC;IAEK,OAAO,GACZ,eAAe,MAAM,KACpB,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CA8CnD;YAEY,MAAM;YAkCN,YAAY;YASZ,OAAO;IAgER,aAAa,CAAC,KAAK,UAAQ;IA0BxC,OAAO,CAAC,OAAO,CAOb;IAEF,OAAO,CAAC,MAAM,CAIZ;IAEF,OAAO,CAAC,YAAY;CAOrB"}
|