@arki/journal 0.0.0-stage → 0.1.1
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 +541 -2
- package/dist/api.d.ts +336 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -0
- package/dist/command.d.ts +27 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/command.js +157 -0
- package/dist/command.js.map +1 -0
- package/dist/debug.d.ts +7 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +8 -0
- package/dist/debug.js.map +1 -0
- package/dist/dot.d.ts +102 -0
- package/dist/dot.d.ts.map +1 -0
- package/dist/dot.js +159 -0
- package/dist/dot.js.map +1 -0
- package/dist/entity.d.ts +56 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/entity.js +174 -0
- package/dist/entity.js.map +1 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +112 -0
- package/dist/errors.js.map +1 -0
- package/dist/event.d.ts +9 -0
- package/dist/event.d.ts.map +1 -0
- package/dist/event.js +19 -0
- package/dist/event.js.map +1 -0
- package/dist/fold.d.ts +29 -0
- package/dist/fold.d.ts.map +1 -0
- package/dist/fold.js +49 -0
- package/dist/fold.js.map +1 -0
- package/dist/handlers.d.ts +26 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +130 -0
- package/dist/handlers.js.map +1 -0
- package/dist/history.d.ts +49 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +259 -0
- package/dist/history.js.map +1 -0
- package/dist/hooks.d.ts +14 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +67 -0
- package/dist/hooks.js.map +1 -0
- package/dist/id.d.ts +18 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +41 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/journal.d.ts +51 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +181 -0
- package/dist/journal.js.map +1 -0
- package/dist/listen.d.ts +37 -0
- package/dist/listen.d.ts.map +1 -0
- package/dist/listen.js +90 -0
- package/dist/listen.js.map +1 -0
- package/dist/orpc.d.ts +21 -0
- package/dist/orpc.d.ts.map +1 -0
- package/dist/orpc.js +57 -0
- package/dist/orpc.js.map +1 -0
- package/dist/pg.d.ts +537 -0
- package/dist/pg.d.ts.map +1 -0
- package/dist/pg.js +80 -0
- package/dist/pg.js.map +1 -0
- package/dist/storage.d.ts +52 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +221 -0
- package/dist/storage.js.map +1 -0
- package/dist/testing.d.ts +19 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +23 -0
- package/dist/testing.js.map +1 -0
- package/dist/transaction-guard.d.ts +2 -0
- package/dist/transaction-guard.d.ts.map +1 -0
- package/dist/transaction-guard.js +38 -0
- package/dist/transaction-guard.js.map +1 -0
- package/dist/types.d.ts +414 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/worker.d.ts +109 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +458 -0
- package/dist/worker.js.map +1 -0
- package/dist/write.d.ts +39 -0
- package/dist/write.d.ts.map +1 -0
- package/dist/write.js +287 -0
- package/dist/write.js.map +1 -0
- package/docs/agent-guide.md +324 -0
- package/llms.txt +40 -0
- package/package.json +99 -4
- package/src/api.ts +465 -0
- package/src/command.ts +242 -0
- package/src/debug.ts +8 -0
- package/src/dot.ts +296 -0
- package/src/entity.ts +377 -0
- package/src/errors.ts +134 -0
- package/src/event.ts +26 -0
- package/src/fold.ts +97 -0
- package/src/handlers.ts +262 -0
- package/src/history.ts +390 -0
- package/src/hooks.ts +85 -0
- package/src/id.ts +54 -0
- package/src/index.ts +76 -0
- package/src/journal.ts +256 -0
- package/src/listen.ts +109 -0
- package/src/orpc.ts +59 -0
- package/src/pg.ts +107 -0
- package/src/storage.ts +277 -0
- package/src/test-support.ts +82 -0
- package/src/testing.ts +23 -0
- package/src/transaction-guard.ts +40 -0
- package/src/types.ts +439 -0
- package/src/worker.ts +609 -0
- package/src/write.ts +434 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** Per code: what happened, and what to do next. The throw site adds the detail (event type, version, column). */
|
|
2
|
+
const MESSAGES = {
|
|
3
|
+
CONFLICT: {
|
|
4
|
+
what: 'the row and its event history cannot accept this write',
|
|
5
|
+
next: 'If another writer appended first, run the command again on the current state. If the row and the history disagree, check with `verify`, and use `repair` only when the history is complete',
|
|
6
|
+
},
|
|
7
|
+
NO_ROW: {
|
|
8
|
+
what: 'no row exists, and this event has no `.create` reducer',
|
|
9
|
+
next: 'Create the entity first, or declare `.create` for this event on the entity',
|
|
10
|
+
},
|
|
11
|
+
ROW_EXISTS: {
|
|
12
|
+
what: 'a row exists, and this event has no `.on` reducer',
|
|
13
|
+
next: 'Write an update event instead, or declare `.on` for this event on the entity',
|
|
14
|
+
},
|
|
15
|
+
UNKNOWN_EVENT: {
|
|
16
|
+
what: 'the entity does not declare this event',
|
|
17
|
+
next: 'Add `.create(Event, ...)` or `.on(Event, ...)` for it on this entity, or write it to the entity that declares it',
|
|
18
|
+
},
|
|
19
|
+
VERSION_MISMATCH: {
|
|
20
|
+
what: 'no row matched the expected version when saving',
|
|
21
|
+
next: 'The row version changed, or the row was removed, after this write locked it. Look for a statement that does this outside the journal (for example a direct update or delete inside `events`), then run the command again',
|
|
22
|
+
},
|
|
23
|
+
NO_HISTORY: {
|
|
24
|
+
what: 'a complete history that starts at version 1 is not available',
|
|
25
|
+
next: 'Replay and repair cannot rebuild this row. Check whether the row existed before the journal, or restore the missing events',
|
|
26
|
+
},
|
|
27
|
+
HISTORY_GAP: {
|
|
28
|
+
what: 'the log has a hole, or the row is newer than a log that says it is alive',
|
|
29
|
+
next: 'An event was lost after it was written. Replay and repair refuse to build a row the real history never produced. Restore the missing event from a backup, or accept the row as it is',
|
|
30
|
+
},
|
|
31
|
+
REPLAY_FAILED: {
|
|
32
|
+
what: 'a stored event cannot be decoded or applied',
|
|
33
|
+
next: 'Check the event schema and the reducers against the stored history',
|
|
34
|
+
},
|
|
35
|
+
UNSTABLE_SCHEMA: {
|
|
36
|
+
what: 'the value changes or fails across JSON storage and a second parse',
|
|
37
|
+
next: 'Return JSON-compatible data that the schema accepts unchanged, because replay parses stored data again',
|
|
38
|
+
},
|
|
39
|
+
ID_MISMATCH: {
|
|
40
|
+
what: 'the entity id is not valid, or differs from its stored spelling',
|
|
41
|
+
next: 'Use a non-empty, well-formed id within the limit, spelled exactly as stored. A uuid must be lower-case and hyphenated',
|
|
42
|
+
},
|
|
43
|
+
VALUE_MISMATCH: {
|
|
44
|
+
what: 'the database stored a different value than the reducer produced',
|
|
45
|
+
next: 'The write was rolled back. Make the reducer produce the value in the form the column stores',
|
|
46
|
+
},
|
|
47
|
+
BAD_DEFINITION: {
|
|
48
|
+
what: 'a declaration is wrong',
|
|
49
|
+
next: 'Fix the declaration named in the detail. It is thrown when the module that declares it loads, so no data was touched',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* An error about one entity's row or history: a write, replay, verify or repair
|
|
54
|
+
* that the journal refuses. Match on `code`, never on the message.
|
|
55
|
+
*
|
|
56
|
+
* Not every error the journal lets out is a `JournalError`:
|
|
57
|
+
* - A wrong definition is a `JournalError` with code `BAD_DEFINITION`, thrown when the
|
|
58
|
+
* declaration runs (a bad name, a bad key column, a duplicate reducer or handler name).
|
|
59
|
+
* - A misuse at run time is a plain `Error` whose message starts with `journal:` (two
|
|
60
|
+
* operations at once on one transaction, `drain` inside a transaction).
|
|
61
|
+
* - Command input, event data or `meta` that fails its schema is a `ZodError`.
|
|
62
|
+
* - An error thrown by a command handler, an `events` function, a reducer or a
|
|
63
|
+
* projection passes through unchanged. A rejected business rule is one of
|
|
64
|
+
* these: a `CommandError`, below.
|
|
65
|
+
* An error thrown by an effect is caught by `drain` and stored on the job.
|
|
66
|
+
*/
|
|
67
|
+
export class JournalError extends Error {
|
|
68
|
+
code;
|
|
69
|
+
kind;
|
|
70
|
+
entityId;
|
|
71
|
+
/** The message reads: `CODE: what happened (kind id): detail. What to do next.` */
|
|
72
|
+
constructor(code, kind, entityId, detail, options) {
|
|
73
|
+
const { what, next } = MESSAGES[code];
|
|
74
|
+
const where = `${code}: ${what} (${kind} ${entityId})`;
|
|
75
|
+
const happened = detail === undefined ? where : `${where}: ${detail}`;
|
|
76
|
+
super(`${happened}. ${next}.`, options);
|
|
77
|
+
this.name = 'JournalError';
|
|
78
|
+
this.code = code;
|
|
79
|
+
this.kind = kind;
|
|
80
|
+
this.entityId = entityId;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function isJournalError(error, code) {
|
|
84
|
+
return error instanceof JournalError && (code === undefined || error.code === code);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A business rule rejected the request. App code throws it: a command handler,
|
|
88
|
+
* or the `events` function of a `write`. The code names the kind of answer and
|
|
89
|
+
* the message is written for the client.
|
|
90
|
+
*
|
|
91
|
+
* It is NOT a `JournalError`. A `JournalError` is the library's own failure: a
|
|
92
|
+
* row or a history it refuses. A `CommandError` is the app's rule saying no.
|
|
93
|
+
* The library never throws one and never changes one: like every error thrown
|
|
94
|
+
* by app code, it leaves `journal.run` as the same instance, after the
|
|
95
|
+
* command's transaction rolled back.
|
|
96
|
+
*
|
|
97
|
+
* Nothing here knows HTTP. The host maps the code to its own transport:
|
|
98
|
+
* `@arki/journal/orpc` does it for oRPC, and any other host matches on the
|
|
99
|
+
* code with `isCommandError`.
|
|
100
|
+
*/
|
|
101
|
+
export class CommandError extends Error {
|
|
102
|
+
code;
|
|
103
|
+
constructor(code, message) {
|
|
104
|
+
super(message);
|
|
105
|
+
this.name = 'CommandError';
|
|
106
|
+
this.code = code;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function isCommandError(error, code) {
|
|
110
|
+
return error instanceof CommandError && (code === undefined || error.code === code);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAcA,kHAAkH;AAClH,MAAM,QAAQ,GAA+E;IAC3F,QAAQ,EAAE;QACR,IAAI,EAAE,wDAAwD;QAC9D,IAAI,EAAE,4LAA4L;KACnM;IACD,MAAM,EAAE;QACN,IAAI,EAAE,wDAAwD;QAC9D,IAAI,EAAE,4EAA4E;KACnF;IACD,UAAU,EAAE;QACV,IAAI,EAAE,mDAAmD;QACzD,IAAI,EAAE,8EAA8E;KACrF;IACD,aAAa,EAAE;QACb,IAAI,EAAE,wCAAwC;QAC9C,IAAI,EAAE,kHAAkH;KACzH;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,iDAAiD;QACvD,IAAI,EAAE,0NAA0N;KACjO;IACD,UAAU,EAAE;QACV,IAAI,EAAE,8DAA8D;QACpE,IAAI,EAAE,4HAA4H;KACnI;IACD,WAAW,EAAE;QACX,IAAI,EAAE,0EAA0E;QAChF,IAAI,EAAE,sLAAsL;KAC7L;IACD,aAAa,EAAE;QACb,IAAI,EAAE,6CAA6C;QACnD,IAAI,EAAE,oEAAoE;KAC3E;IACD,eAAe,EAAE;QACf,IAAI,EAAE,mEAAmE;QACzE,IAAI,EAAE,wGAAwG;KAC/G;IACD,WAAW,EAAE;QACX,IAAI,EAAE,iEAAiE;QACvE,IAAI,EAAE,uHAAuH;KAC9H;IACD,cAAc,EAAE;QACd,IAAI,EAAE,iEAAiE;QACvE,IAAI,EAAE,6FAA6F;KACpG;IACD,cAAc,EAAE;QACd,IAAI,EAAE,wBAAwB;QAC9B,IAAI,EAAE,sHAAsH;KAC7H;CACF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,CAAmB;IACvB,IAAI,CAAS;IACb,QAAQ,CAAS;IAE1B,mFAAmF;IACnF,YAAY,IAAsB,EAAE,IAAY,EAAE,QAAgB,EAAE,MAAe,EAAE,OAAsB;QACzG,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,GAAG,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,QAAQ,GAAG,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,MAAM,EAAE,CAAC;QACtE,KAAK,CAAC,GAAG,QAAQ,KAAK,IAAI,GAAG,EAAE,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAuB;IACpE,OAAO,KAAK,YAAY,YAAY,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACtF,CAAC;AAKD;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,CAAmB;IAEhC,YAAY,IAAsB,EAAE,OAAe;QACjD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAuB;IACpE,OAAO,KAAK,YAAY,YAAY,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACtF,CAAC"}
|
package/dist/event.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { EventDef, EventOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Declare an event. Calling the result validates the data and returns a pending event.
|
|
5
|
+
* `opts.erased` gives the event's erased form for `forget`; see the README, Erasure.
|
|
6
|
+
* `opts.upcasters` lifts data stored under older schemas; see the README, Changing events later.
|
|
7
|
+
*/
|
|
8
|
+
export declare function defineEvent<T extends string, S extends z.ZodType, const U extends readonly z.ZodType[] = readonly []>(type: T, schema: S, opts?: EventOptions<S, U>): EventDef<T, S>;
|
|
9
|
+
//# sourceMappingURL=event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAqB,MAAM,YAAY,CAAC;AAG5E;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,SAAS,EAAE,EACnH,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,CAAC,EACT,IAAI,CAAC,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,GACxB,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAWhB"}
|
package/dist/event.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { assertStoredName } from './id.js';
|
|
2
|
+
/**
|
|
3
|
+
* Declare an event. Calling the result validates the data and returns a pending event.
|
|
4
|
+
* `opts.erased` gives the event's erased form for `forget`; see the README, Erasure.
|
|
5
|
+
* `opts.upcasters` lifts data stored under older schemas; see the README, Changing events later.
|
|
6
|
+
*/
|
|
7
|
+
export function defineEvent(type, schema, opts) {
|
|
8
|
+
assertStoredName('event type', type);
|
|
9
|
+
const upcasters = opts?.upcasters ?? [];
|
|
10
|
+
const make = (data) => ({ type, data: schema.parse(data) });
|
|
11
|
+
return Object.assign(make, {
|
|
12
|
+
type,
|
|
13
|
+
schema,
|
|
14
|
+
version: upcasters.length + 1,
|
|
15
|
+
upcasters,
|
|
16
|
+
...(opts?.erased === undefined ? {} : { erased: opts.erased }),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.js","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,IAAO,EACP,MAAS,EACT,IAAyB;IAEzB,gBAAgB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,GAAwB,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC;IAC7D,MAAM,IAAI,GAAG,CAAC,IAAgB,EAA2B,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjG,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QACzB,IAAI;QACJ,MAAM;QACN,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;QAC7B,SAAS;QACT,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC"}
|
package/dist/fold.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fold that write and replay share: apply recorded events to a row in memory.
|
|
3
|
+
* Also the two lookups by event type. Pure functions: no database access, no state.
|
|
4
|
+
* May import `entity` and leaf files. Must not import `journal`, `api`, `write`,
|
|
5
|
+
* `command`, `history`, `storage` or `worker`.
|
|
6
|
+
*/
|
|
7
|
+
import type { AnyEntity, AnyEventDef, EntityDef, EntityId, EntityTable, Recorded, RecordedOf, ReducerRow, Row } from './types.js';
|
|
8
|
+
type RecordedEvent<MetaOut> = Recorded<string, unknown, MetaOut>;
|
|
9
|
+
export declare const findEventDefinition: <MetaOut>(entities: readonly AnyEntity<MetaOut>[], kind: string, type: string) => AnyEventDef | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Fold events onto a starting state. A create event on a row, or a non-create
|
|
12
|
+
* event with no row, is `ROW_EXISTS` / `NO_ROW` during a write and
|
|
13
|
+
* `REPLAY_FAILED` when reading the log back. `recreated` is true when the state
|
|
14
|
+
* passed through `null` and ended non-null: the row is a new incarnation.
|
|
15
|
+
*/
|
|
16
|
+
export declare const fold: <Tbl extends EntityTable, Key extends string, MetaOut>(entity: EntityDef<string, Tbl, AnyEventDef, MetaOut, Key>, start: Row<Tbl> | null, id: EntityId<Tbl, Key>, history: readonly RecordedEvent<MetaOut>[], mode: 'write' | 'replay') => {
|
|
17
|
+
readonly row: ReducerRow<Tbl, Key> | null;
|
|
18
|
+
readonly recreated: boolean;
|
|
19
|
+
/** The columns the reducers set in this fold (since the last create). */
|
|
20
|
+
readonly reducerColumns: ReadonlySet<string>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Narrows a decoded event to the entity's own event union. It is a type guard, not
|
|
24
|
+
* a filter for bad data: `decodeStoredEvent` has already thrown for a type the
|
|
25
|
+
* entity does not declare. Do not use it to skip stored events that fail to decode.
|
|
26
|
+
*/
|
|
27
|
+
export declare const hasDeclaredEventType: <E extends AnyEventDef, Id extends string, MetaOut>(entity: EntityDef<string, EntityTable, E, MetaOut, string>, event: Recorded<string, unknown, MetaOut, Id>) => event is RecordedOf<E, MetaOut, Id>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=fold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../src/fold.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,SAAS,EACT,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,UAAU,EACV,UAAU,EAEV,GAAG,EACJ,MAAM,YAAY,CAAC;AAIpB,KAAK,aAAa,CAAC,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEjE,eAAO,MAAM,mBAAmB,GAAI,OAAO,YAC/B,SAAS,SAAS,CAAC,OAAO,CAAC,EAAE,QACjC,MAAM,QACN,MAAM,KACX,WAAW,GAAG,SAA4E,CAAC;AAE9F;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,GAAG,SAAS,WAAW,EAAE,GAAG,SAAS,MAAM,EAAE,OAAO,UAC/D,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,SAClD,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,MAClB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,WACb,SAAS,aAAa,CAAC,OAAO,CAAC,EAAE,QACpC,OAAO,GAAG,QAAQ,KACvB;IACD,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAyC9C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,WAAW,EAAE,EAAE,SAAS,MAAM,EAAE,OAAO,UAC5E,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,SACnD,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,KAC5C,KAAK,IAAI,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAkC,CAAC"}
|
package/dist/fold.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { reduceOne, withEntityIdentity } from './entity.js';
|
|
2
|
+
import { JournalError } from './errors.js';
|
|
3
|
+
export const findEventDefinition = (entities, kind, type) => entities.find(entity => entity.kind === kind)?.events.get(type);
|
|
4
|
+
/**
|
|
5
|
+
* Fold events onto a starting state. A create event on a row, or a non-create
|
|
6
|
+
* event with no row, is `ROW_EXISTS` / `NO_ROW` during a write and
|
|
7
|
+
* `REPLAY_FAILED` when reading the log back. `recreated` is true when the state
|
|
8
|
+
* passed through `null` and ended non-null: the row is a new incarnation.
|
|
9
|
+
*/
|
|
10
|
+
export const fold = (entity, start, id, history, mode) => {
|
|
11
|
+
let state = start;
|
|
12
|
+
let deleted = false;
|
|
13
|
+
const reducerColumns = new Set();
|
|
14
|
+
for (const event of history) {
|
|
15
|
+
const reducers = entity.reducers.get(event.type);
|
|
16
|
+
// A write names the exact refusal. A replay reports each one as REPLAY_FAILED and says why.
|
|
17
|
+
const at = `event "${event.type}" at version ${event.version}`;
|
|
18
|
+
if (reducers === undefined) {
|
|
19
|
+
throw mode === 'write'
|
|
20
|
+
? new JournalError('UNKNOWN_EVENT', entity.kind, id, at)
|
|
21
|
+
: new JournalError('REPLAY_FAILED', entity.kind, id, `${at} has no reducer on this entity`);
|
|
22
|
+
}
|
|
23
|
+
if (state !== null && reducers.on === undefined) {
|
|
24
|
+
throw mode === 'write'
|
|
25
|
+
? new JournalError('ROW_EXISTS', entity.kind, id, at)
|
|
26
|
+
: new JournalError('REPLAY_FAILED', entity.kind, id, `${at} has no .on reducer, but the history has a row at that point`);
|
|
27
|
+
}
|
|
28
|
+
if (state === null && reducers.create === undefined) {
|
|
29
|
+
throw mode === 'write'
|
|
30
|
+
? new JournalError('NO_ROW', entity.kind, id, at)
|
|
31
|
+
: new JournalError('REPLAY_FAILED', entity.kind, id, `${at} has no .create reducer, but the history has no row at that point`);
|
|
32
|
+
}
|
|
33
|
+
state = reduceOne(entity, state, id, event, reducerColumns);
|
|
34
|
+
if (state === null)
|
|
35
|
+
deleted = true;
|
|
36
|
+
}
|
|
37
|
+
if (state === null)
|
|
38
|
+
return { row: null, recreated: false, reducerColumns };
|
|
39
|
+
const last = history.at(-1);
|
|
40
|
+
const version = last === undefined ? (start === null ? 0 : start.version) : last.version;
|
|
41
|
+
return { row: withEntityIdentity(entity, state, id, version), recreated: deleted, reducerColumns };
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Narrows a decoded event to the entity's own event union. It is a type guard, not
|
|
45
|
+
* a filter for bad data: `decodeStoredEvent` has already thrown for a type the
|
|
46
|
+
* entity does not declare. Do not use it to skip stored events that fail to decode.
|
|
47
|
+
*/
|
|
48
|
+
export const hasDeclaredEventType = (entity, event) => entity.events.has(event.type);
|
|
49
|
+
//# sourceMappingURL=fold.js.map
|
package/dist/fold.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fold.js","sourceRoot":"","sources":["../src/fold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AnyEntity, DbHandle, Effect, EffectHandlers, EffectSchedule, Need, Needs, Projection, ProjectionHandlers, ProjectionOptions, Services, Tap, TapHandlers } from './types.js';
|
|
2
|
+
export declare function defineProjection<E extends AnyEntity<Meta>, Meta, Tx extends DbHandle>(name: string, entity: E | readonly E[], on: NoInfer<ProjectionHandlers<E, Meta, Tx>>, opts?: ProjectionOptions): Projection<Meta, Tx>;
|
|
3
|
+
/** Declare one thing an effect needs: `j.effect('order-mail', order, { mailer: need<Mailer>() }, { ... })`. */
|
|
4
|
+
export declare const need: <T>() => Need<T>;
|
|
5
|
+
/** Attach a schedule to an effect, checked once: a job row must be able to hold it. */
|
|
6
|
+
export declare function withSchedule<Meta, AppDb, Run, N extends Needs>(effect: Omit<Effect<Meta, AppDb, Run, N>, 'schedule' | 'withSchedule'>, schedule: EffectSchedule<Meta>): Effect<Meta, AppDb, Run, N>;
|
|
7
|
+
/** `j.effect` for one journal. Three arguments: the effect needs nothing. Four: the third lists its needs. */
|
|
8
|
+
export declare function effectFor<Meta, AppDb, Run>(): {
|
|
9
|
+
<E extends AnyEntity<Meta>>(name: string, entity: E | readonly E[], on: NoInfer<EffectHandlers<E, Meta, AppDb, Run>>): Effect<Meta, AppDb, Run>;
|
|
10
|
+
<E extends AnyEntity<Meta>, N extends Needs>(name: string, entity: E | readonly E[], needs: N, on: NoInfer<EffectHandlers<E, Meta, AppDb, Run, N>>): Effect<Meta, AppDb, Run, N>;
|
|
11
|
+
};
|
|
12
|
+
/** `j.tap` for one journal. Three arguments: the tap needs nothing. Four: the third lists its needs. */
|
|
13
|
+
export declare function tapFor<Meta, AppDb>(): {
|
|
14
|
+
<E extends AnyEntity<Meta>>(name: string, entity: E | readonly E[], on: NoInfer<TapHandlers<E, Meta, AppDb>>): Tap<Meta, AppDb>;
|
|
15
|
+
<E extends AnyEntity<Meta>, N extends Needs>(name: string, entity: E | readonly E[], needs: N, on: NoInfer<TapHandlers<E, Meta, AppDb, N>>): Tap<Meta, AppDb, N>;
|
|
16
|
+
};
|
|
17
|
+
/** Give one tap its services, as `bindEffect` does for an effect. */
|
|
18
|
+
export declare function bindTap<Meta, AppDb>(tap: Tap<Meta, AppDb, Needs>, services: Services): Tap<Meta, AppDb>;
|
|
19
|
+
/**
|
|
20
|
+
* Give one effect its services. The result needs nothing more, so the worker can run it.
|
|
21
|
+
* `services` may hold more than this effect needs: each need is read by its name.
|
|
22
|
+
* This is the only place a need meets its value. `j.compose` calls it with the app's
|
|
23
|
+
* `services`; the Dot bridge calls it with the services of the feature that lists the effect.
|
|
24
|
+
*/
|
|
25
|
+
export declare function bindEffect<Meta, AppDb, Run>(effect: Effect<Meta, AppDb, Run, Needs>, services: Services): Effect<Meta, AppDb, Run>;
|
|
26
|
+
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { JournalError } from './errors.js';
|
|
2
|
+
import { assertStoredName } from './id.js';
|
|
3
|
+
/** An entity has a `kind`; an array of entities does not. */
|
|
4
|
+
function toList(entity) {
|
|
5
|
+
return 'kind' in entity ? [entity] : entity;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Only an own property counts as a handler. A plain object inherits `toString`,
|
|
9
|
+
* `constructor` and friends from `Object.prototype`; an event type with one of
|
|
10
|
+
* those names must not find a handler nobody declared.
|
|
11
|
+
*/
|
|
12
|
+
function ownHandler(handlers, type) {
|
|
13
|
+
return Object.hasOwn(handlers, type) ? handlers[type] : undefined;
|
|
14
|
+
}
|
|
15
|
+
function declaredTypes(handlers) {
|
|
16
|
+
return new Set(Object.keys(handlers).filter(key => ownHandler(handlers, key) !== undefined));
|
|
17
|
+
}
|
|
18
|
+
export function defineProjection(name, entity, on, opts) {
|
|
19
|
+
const handlers = on;
|
|
20
|
+
if (opts?.lane === 'async')
|
|
21
|
+
assertStoredName('effect name', name);
|
|
22
|
+
return {
|
|
23
|
+
name,
|
|
24
|
+
entities: toList(entity),
|
|
25
|
+
types: declaredTypes(handlers),
|
|
26
|
+
lane: opts?.lane ?? 'inline',
|
|
27
|
+
async run(tx, event) {
|
|
28
|
+
const handler = ownHandler(handlers, event.type);
|
|
29
|
+
if (handler !== undefined)
|
|
30
|
+
await handler(tx, event);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Every effect handler gets these. A need must not take one of their names. */
|
|
35
|
+
const builtInContext = new Set(['jobId', 'signal', 'db', 'run']);
|
|
36
|
+
/** Declare one thing an effect needs: `j.effect('order-mail', order, { mailer: need<Mailer>() }, { ... })`. */
|
|
37
|
+
export const need = () => ({});
|
|
38
|
+
function defineEffect(name, entity, needs, on) {
|
|
39
|
+
assertStoredName('effect name', name);
|
|
40
|
+
for (const key of Object.keys(needs)) {
|
|
41
|
+
if (builtInContext.has(key))
|
|
42
|
+
throw new JournalError('BAD_DEFINITION', 'effect', name, `journal: effect "${name}" has a need named "${key}", but the handler context already has "jobId", "signal", "db" and "run"; give the need another name`);
|
|
43
|
+
}
|
|
44
|
+
const handlers = on;
|
|
45
|
+
return withSchedule({
|
|
46
|
+
name,
|
|
47
|
+
entities: toList(entity),
|
|
48
|
+
types: declaredTypes(handlers),
|
|
49
|
+
needs,
|
|
50
|
+
async run(event, ctx) {
|
|
51
|
+
const handler = ownHandler(handlers, event.type);
|
|
52
|
+
// A job was queued for this type. Running nothing would acknowledge it silently.
|
|
53
|
+
if (handler === undefined)
|
|
54
|
+
throw new Error(`effect "${name}" has no handler for "${event.type}", but a job was queued for it; add a handler for this event to the effect, or delete the job if the event is no longer handled`);
|
|
55
|
+
await handler(event, ctx);
|
|
56
|
+
},
|
|
57
|
+
}, {});
|
|
58
|
+
}
|
|
59
|
+
const INT4_MAX = 2_147_483_647;
|
|
60
|
+
/** Attach a schedule to an effect, checked once: a job row must be able to hold it. */
|
|
61
|
+
export function withSchedule(effect, schedule) {
|
|
62
|
+
const { priority = 0, delaySeconds = 0 } = schedule;
|
|
63
|
+
if (!Number.isInteger(priority) || Math.abs(priority) > INT4_MAX)
|
|
64
|
+
throw new JournalError('BAD_DEFINITION', 'effect', effect.name, `journal: effect "${effect.name}": priority must be an integer within 32 bits, got ${String(priority)}`);
|
|
65
|
+
if (!Number.isFinite(delaySeconds) || delaySeconds < 0)
|
|
66
|
+
throw new JournalError('BAD_DEFINITION', 'effect', effect.name, `journal: effect "${effect.name}": delaySeconds must be a finite number of seconds, zero or more, got ${String(delaySeconds)}`);
|
|
67
|
+
if (schedule.delaySeconds !== undefined && schedule.runAt !== undefined)
|
|
68
|
+
throw new JournalError('BAD_DEFINITION', 'effect', effect.name, `journal: effect "${effect.name}": give delaySeconds or runAt, not both`);
|
|
69
|
+
return {
|
|
70
|
+
...effect,
|
|
71
|
+
schedule,
|
|
72
|
+
withSchedule: next => withSchedule(effect, next),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** `j.effect` for one journal. Three arguments: the effect needs nothing. Four: the third lists its needs. */
|
|
76
|
+
export function effectFor() {
|
|
77
|
+
function effect(name, entity, ...rest) {
|
|
78
|
+
return rest.length === 1 ? defineEffect(name, entity, {}, rest[0]) : defineEffect(name, entity, rest[0], rest[1]);
|
|
79
|
+
}
|
|
80
|
+
return effect;
|
|
81
|
+
}
|
|
82
|
+
function defineTap(name, entity, needs, on) {
|
|
83
|
+
assertStoredName('effect name', name);
|
|
84
|
+
if (Object.hasOwn(needs, 'db'))
|
|
85
|
+
throw new JournalError('BAD_DEFINITION', 'tap', name, `journal: tap "${name}" has a need named "db", but the handler context already has "db"; give the need another name`);
|
|
86
|
+
const handlers = on;
|
|
87
|
+
return {
|
|
88
|
+
name,
|
|
89
|
+
entities: toList(entity),
|
|
90
|
+
types: declaredTypes(handlers),
|
|
91
|
+
needs,
|
|
92
|
+
async run(event, ctx) {
|
|
93
|
+
await ownHandler(handlers, event.type)?.(event, ctx);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** `j.tap` for one journal. Three arguments: the tap needs nothing. Four: the third lists its needs. */
|
|
98
|
+
export function tapFor() {
|
|
99
|
+
function tap(name, entity, ...rest) {
|
|
100
|
+
return rest.length === 1 ? defineTap(name, entity, {}, rest[0]) : defineTap(name, entity, rest[0], rest[1]);
|
|
101
|
+
}
|
|
102
|
+
return tap;
|
|
103
|
+
}
|
|
104
|
+
/** Give one tap its services, as `bindEffect` does for an effect. */
|
|
105
|
+
export function bindTap(tap, services) {
|
|
106
|
+
const given = {};
|
|
107
|
+
for (const key of Object.keys(tap.needs)) {
|
|
108
|
+
if (!Object.hasOwn(services, key) || services[key] === undefined)
|
|
109
|
+
throw new JournalError('BAD_DEFINITION', 'tap', tap.name, `journal: tap "${tap.name}" needs "${key}", but no service with that name was given; pass it as j.compose({ services: { ${key} } }), or in a Dot app add it to the \`needs\` of the feature that lists this tap`);
|
|
110
|
+
given[key] = services[key];
|
|
111
|
+
}
|
|
112
|
+
return { ...tap, needs: {}, run: (event, ctx) => tap.run(event, { ...given, ...ctx }) };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Give one effect its services. The result needs nothing more, so the worker can run it.
|
|
116
|
+
* `services` may hold more than this effect needs: each need is read by its name.
|
|
117
|
+
* This is the only place a need meets its value. `j.compose` calls it with the app's
|
|
118
|
+
* `services`; the Dot bridge calls it with the services of the feature that lists the effect.
|
|
119
|
+
*/
|
|
120
|
+
export function bindEffect(effect, services) {
|
|
121
|
+
const given = {};
|
|
122
|
+
for (const key of Object.keys(effect.needs)) {
|
|
123
|
+
// Without this check the effect would start, and every job would fail on `undefined`.
|
|
124
|
+
if (!Object.hasOwn(services, key) || services[key] === undefined)
|
|
125
|
+
throw new JournalError('BAD_DEFINITION', 'effect', effect.name, `journal: effect "${effect.name}" needs "${key}", but no service with that name was given; pass it as j.compose({ services: { ${key} } }), or in a Dot app add it to the \`needs\` of the feature that lists this effect`);
|
|
126
|
+
given[key] = services[key];
|
|
127
|
+
}
|
|
128
|
+
return withSchedule({ ...effect, needs: {}, run: (event, ctx) => effect.run(event, { ...given, ...ctx }) }, effect.schedule);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,6DAA6D;AAC7D,SAAS,MAAM,CAAsC,MAAwB;IAC3E,OAAO,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAI,QAAoC,EAAE,IAAY;IACvE,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACpE,CAAC;AAED,SAAS,aAAa,CAAC,QAA0C;IAC/D,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,MAAwB,EACxB,EAA4C,EAC5C,IAAwB;IAExB,MAAM,QAAQ,GAAoG,EAAE,CAAC;IACrH,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO;QAAE,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAClE,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC;QAC9B,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,QAAQ;QAC5B,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK;YACjB,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,OAAO,KAAK,SAAS;gBAAE,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACtD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAEjE,+GAA+G;AAC/G,MAAM,CAAC,MAAM,IAAI,GAAG,GAAe,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAE3C,SAAS,YAAY,CACnB,IAAY,EACZ,MAAwB,EACxB,KAAQ,EACR,EAA0C;IAE1C,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YACzB,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,QAAQ,EACR,IAAI,EACJ,oBAAoB,IAAI,uBAAuB,GAAG,sGAAsG,CACzJ,CAAC;IACN,CAAC;IACD,MAAM,QAAQ,GAKV,EAAE,CAAC;IACP,OAAO,YAAY,CACjB;QACE,IAAI;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC;QAC9B,KAAK;QACL,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG;YAClB,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACjD,iFAAiF;YACjF,IAAI,OAAO,KAAK,SAAS;gBACvB,MAAM,IAAI,KAAK,CACb,WAAW,IAAI,yBAAyB,KAAK,CAAC,IAAI,iIAAiI,CACpL,CAAC;YACJ,MAAM,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;KACF,EACD,EAAE,CACH,CAAC;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,aAAa,CAAC;AAE/B,uFAAuF;AACvF,MAAM,UAAU,YAAY,CAC1B,MAAsE,EACtE,QAA8B;IAE9B,MAAM,EAAE,QAAQ,GAAG,CAAC,EAAE,YAAY,GAAG,CAAC,EAAE,GAAG,QAAQ,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,QAAQ;QAC9D,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,QAAQ,EACR,MAAM,CAAC,IAAI,EACX,oBAAoB,MAAM,CAAC,IAAI,sDAAsD,MAAM,CAAC,QAAQ,CAAC,EAAE,CACxG,CAAC;IACJ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC;QACpD,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,QAAQ,EACR,MAAM,CAAC,IAAI,EACX,oBAAoB,MAAM,CAAC,IAAI,yEAAyE,MAAM,CAAC,YAAY,CAAC,EAAE,CAC/H,CAAC;IACJ,IAAI,QAAQ,CAAC,YAAY,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS;QACrE,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,QAAQ,EACR,MAAM,CAAC,IAAI,EACX,oBAAoB,MAAM,CAAC,IAAI,yCAAyC,CACzE,CAAC;IACJ,OAAO;QACL,GAAG,MAAM;QACT,QAAQ;QACR,YAAY,EAAE,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,8GAA8G;AAC9G,MAAM,UAAU,SAAS;IAYvB,SAAS,MAAM,CACb,IAAY,EACZ,MAAwB,EACxB,GAAG,IAAwG;QAE3G,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACpH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,SAAS,CAChB,IAAY,EACZ,MAAwB,EACxB,KAAQ,EACR,EAAkC;IAElC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC;QAC5B,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,KAAK,EACL,IAAI,EACJ,iBAAiB,IAAI,+FAA+F,CACrH,CAAC;IACJ,MAAM,QAAQ,GAEV,EAAE,CAAC;IACP,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QACxB,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC;QAC9B,KAAK;QACL,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG;YAClB,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,MAAM;IAYpB,SAAS,GAAG,CACV,IAAY,EACZ,MAAwB,EACxB,GAAG,IAAwF;QAE3F,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,OAAO,CAAc,GAA4B,EAAE,QAAkB;IACnF,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS;YAC9D,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,KAAK,EACL,GAAG,CAAC,IAAI,EACR,iBAAiB,GAAG,CAAC,IAAI,YAAY,GAAG,kFAAkF,GAAG,mFAAmF,CACjN,CAAC;QACJ,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC;AAC1F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,MAAuC,EACvC,QAAkB;IAElB,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,sFAAsF;QACtF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS;YAC9D,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,QAAQ,EACR,MAAM,CAAC,IAAI,EACX,oBAAoB,MAAM,CAAC,IAAI,YAAY,GAAG,kFAAkF,GAAG,sFAAsF,CAC1N,CAAC;QACJ,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,YAAY,CACjB,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,GAAG,EAAE,CAAC,EAAE,EACtF,MAAM,CAAC,QAAQ,CAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the log: decoding stored events, the log queries, history-completeness
|
|
3
|
+
* checks, and `replay`, `verify`, `repair`, `rerun`.
|
|
4
|
+
* May import `fold`, `storage`, leaf files, and result types from `api`.
|
|
5
|
+
* Must not import `journal`, `write`, `command` or `worker`.
|
|
6
|
+
*/
|
|
7
|
+
import type { z } from 'zod';
|
|
8
|
+
import type { ForgetResult, ReplayResult, VerifyResult } from './api.js';
|
|
9
|
+
import type { JournalTables } from './pg.js';
|
|
10
|
+
import type { AnyEventDef, DbHandle, EntityDef, EntityId, EntityTable, Projection, Recorded, Row, TransactionSource } from './types.js';
|
|
11
|
+
/** What the log functions need from the journal: the meta schema and the journal tables. */
|
|
12
|
+
export type HistoryDeps<Meta extends z.ZodObject> = {
|
|
13
|
+
readonly metaSchema: Meta;
|
|
14
|
+
readonly tables: JournalTables;
|
|
15
|
+
};
|
|
16
|
+
export declare function createHistory<Meta extends z.ZodObject>(deps: HistoryDeps<Meta>): {
|
|
17
|
+
decodeStoredEvent: <Id extends string>(row: {
|
|
18
|
+
causeSeq: number | null;
|
|
19
|
+
commandId: string | null;
|
|
20
|
+
correlationId: string | null;
|
|
21
|
+
data: unknown;
|
|
22
|
+
entityId: string;
|
|
23
|
+
erasedAt: Date | null;
|
|
24
|
+
id: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
meta: unknown;
|
|
27
|
+
recordedAt: Date;
|
|
28
|
+
schemaVersion: number;
|
|
29
|
+
seq: number;
|
|
30
|
+
type: string;
|
|
31
|
+
version: number;
|
|
32
|
+
}, def: AnyEventDef | undefined, id: Id) => Recorded<string, unknown, z.output<Meta>, Id>;
|
|
33
|
+
highestVersion: (db: DbHandle, kind: string, id: string) => Promise<number>;
|
|
34
|
+
assertLogSaysGone: <Tbl extends EntityTable, E extends AnyEventDef, Key extends string>(db: DbHandle, entity: EntityDef<string, Tbl, E, z.output<Meta>, Key>, id: EntityId<Tbl, Key>, latestLogVersion: number) => Promise<void>;
|
|
35
|
+
replay: <Tbl extends EntityTable, E extends AnyEventDef, Key extends string>(db: DbHandle, entity: EntityDef<string, Tbl, E, z.output<Meta>, Key>, id: EntityId<Tbl, Key>, opts?: {
|
|
36
|
+
readonly throughVersion?: number;
|
|
37
|
+
}) => Promise<ReplayResult<Tbl, E, z.output<Meta>, Key>>;
|
|
38
|
+
verify: <Tbl extends EntityTable, E extends AnyEventDef, Key extends string>(db: DbHandle, entity: EntityDef<string, Tbl, E, z.output<Meta>, Key>, id: EntityId<Tbl, Key>) => Promise<VerifyResult<Tbl, Key>>;
|
|
39
|
+
repair: <Tbl extends EntityTable, E extends AnyEventDef, Key extends string>(db: DbHandle, entity: EntityDef<string, Tbl, E, z.output<Meta>, Key>, id: EntityId<Tbl, Key>) => Promise<Row<Tbl> | null>;
|
|
40
|
+
forget: <Tbl extends EntityTable, E extends AnyEventDef, Key extends string>(db: DbHandle, entity: EntityDef<string, Tbl, E, z.output<Meta>, Key>, id: EntityId<Tbl, Key>) => Promise<ForgetResult<Tbl>>;
|
|
41
|
+
rerun: <Tx extends TransactionSource<Tx>>(db: TransactionSource<Tx>, projection: Projection<z.output<Meta>, Tx>, opts?: {
|
|
42
|
+
readonly afterSeq?: number;
|
|
43
|
+
readonly batchSize?: number;
|
|
44
|
+
}) => Promise<{
|
|
45
|
+
readonly events: number;
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
48
|
+
export type History<Meta extends z.ZodObject> = ReturnType<typeof createHistory<Meta>>;
|
|
49
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
|