@requence/event-sourcing 0.0.1 → 1.0.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 +81 -0
- package/dist/chunk-24xjphpk.js +51 -0
- package/dist/chunk-24xjphpk.js.map +10 -0
- package/dist/chunk-3t1vzbab.js +88 -0
- package/dist/chunk-3t1vzbab.js.map +10 -0
- package/dist/chunk-6q8mr1sg.js +13 -0
- package/dist/chunk-6q8mr1sg.js.map +10 -0
- package/dist/chunk-9psg50cb.js +22 -0
- package/dist/chunk-9psg50cb.js.map +10 -0
- package/dist/chunk-dgzw1nj9.js +50 -0
- package/dist/chunk-dgzw1nj9.js.map +10 -0
- package/dist/chunk-gq80tgvf.js +70 -0
- package/dist/chunk-gq80tgvf.js.map +10 -0
- package/dist/chunk-jd4q8fcf.js +11 -0
- package/dist/chunk-jd4q8fcf.js.map +10 -0
- package/dist/chunk-m07vf8q9.js +210 -0
- package/dist/chunk-m07vf8q9.js.map +10 -0
- package/dist/chunk-ncpk70p4.js +313 -0
- package/dist/chunk-ncpk70p4.js.map +10 -0
- package/dist/chunk-pmfzjavb.js +49 -0
- package/dist/chunk-pmfzjavb.js.map +10 -0
- package/dist/chunk-qdczt68t.js +84 -0
- package/dist/chunk-qdczt68t.js.map +10 -0
- package/dist/chunk-tfy01fah.js +34 -0
- package/dist/chunk-tfy01fah.js.map +10 -0
- package/dist/chunk-tkvj6x8k.js +389 -0
- package/dist/chunk-tkvj6x8k.js.map +10 -0
- package/dist/chunk-ty9bhdvx.js +53 -0
- package/dist/chunk-ty9bhdvx.js.map +10 -0
- package/dist/chunk-wmz6a1jk.js +233 -0
- package/dist/chunk-wmz6a1jk.js.map +10 -0
- package/dist/chunk-x90g388y.js +22 -0
- package/dist/chunk-x90g388y.js.map +10 -0
- package/dist/chunk-z388cd25.js +91 -0
- package/dist/chunk-z388cd25.js.map +10 -0
- package/dist/chunk-zf3x573k.js +65 -0
- package/dist/chunk-zf3x573k.js.map +10 -0
- package/dist/createAggregateRoot.d.ts +191 -0
- package/dist/createAggregateRoot.d.ts.map +1 -0
- package/dist/createAggregateRoot.js +18 -0
- package/dist/createAggregateRoot.js.map +9 -0
- package/dist/createCheckpointApi.d.ts +38 -0
- package/dist/createCheckpointApi.d.ts.map +1 -0
- package/dist/createCheckpointApi.js +9 -0
- package/dist/createCheckpointApi.js.map +9 -0
- package/dist/createEventListener.d.ts +46 -0
- package/dist/createEventListener.d.ts.map +1 -0
- package/dist/createEventListener.js +12 -0
- package/dist/createEventListener.js.map +9 -0
- package/dist/createEventStore.d.ts +57 -0
- package/dist/createEventStore.d.ts.map +1 -0
- package/dist/createEventStore.js +20 -0
- package/dist/createEventStore.js.map +9 -0
- package/dist/createProcessManager.d.ts +70 -0
- package/dist/createProcessManager.d.ts.map +1 -0
- package/dist/createProcessManager.js +16 -0
- package/dist/createProcessManager.js.map +9 -0
- package/dist/createProjection.d.ts +120 -0
- package/dist/createProjection.d.ts.map +1 -0
- package/dist/createProjection.js +16 -0
- package/dist/createProjection.js.map +9 -0
- package/dist/drizzle/index.d.ts +449 -0
- package/dist/drizzle/index.d.ts.map +1 -0
- package/dist/drizzle/index.js +325 -0
- package/dist/drizzle/index.js.map +10 -0
- package/dist/drizzle/postgres.d.ts +686 -0
- package/dist/drizzle/postgres.d.ts.map +1 -0
- package/dist/drizzle/postgres.js +18 -0
- package/dist/drizzle/postgres.js.map +9 -0
- package/dist/errors.d.ts +53 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +9 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +9 -0
- package/dist/lock.d.ts +6 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +8 -0
- package/dist/lock.js.map +9 -0
- package/dist/memory/index.d.ts +15 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +178 -0
- package/dist/memory/index.js.map +10 -0
- package/dist/redis/index.d.ts +2 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/index.js +8 -0
- package/dist/redis/index.js.map +9 -0
- package/dist/redis/redisLock.d.ts +4 -0
- package/dist/redis/redisLock.d.ts.map +1 -0
- package/dist/redis/redisLock.js +8 -0
- package/dist/redis/redisLock.js.map +9 -0
- package/dist/refresh.d.ts +4 -0
- package/dist/refresh.d.ts.map +1 -0
- package/dist/refresh.js +13 -0
- package/dist/refresh.js.map +9 -0
- package/dist/replay.d.ts +4 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +13 -0
- package/dist/replay.js.map +9 -0
- package/dist/snapshotGenerator.d.ts +79 -0
- package/dist/snapshotGenerator.d.ts.map +1 -0
- package/dist/snapshotGenerator.js +9 -0
- package/dist/snapshotGenerator.js.map +9 -0
- package/dist/superjson.d.ts +5 -0
- package/dist/superjson.d.ts.map +1 -0
- package/dist/superjson.js +12 -0
- package/dist/superjson.js.map +9 -0
- package/dist/transaction.d.ts +13 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +8 -0
- package/dist/transaction.js.map +9 -0
- package/dist/utilityTypes.d.js +2 -0
- package/dist/utilityTypes.d.js.map +9 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +10 -0
- package/dist/utils.js.map +9 -0
- package/dist/wrappedEventBuilder.d.ts +37 -0
- package/dist/wrappedEventBuilder.d.ts.map +1 -0
- package/dist/wrappedEventBuilder.js +12 -0
- package/dist/wrappedEventBuilder.js.map +9 -0
- package/package.json +87 -1
- package/src/createAggregateRoot.ts +902 -0
- package/src/createCheckpointApi.ts +106 -0
- package/src/createEventListener.ts +118 -0
- package/src/createEventStore.ts +446 -0
- package/src/createProcessManager.ts +363 -0
- package/src/createProjection.ts +577 -0
- package/src/drizzle/index.ts +449 -0
- package/src/drizzle/postgres.ts +120 -0
- package/src/errors.ts +85 -0
- package/src/index.ts +45 -0
- package/src/lock.ts +58 -0
- package/src/memory/index.ts +224 -0
- package/src/redis/index.ts +1 -0
- package/src/redis/redisLock.ts +61 -0
- package/src/refresh.ts +19 -0
- package/src/replay.ts +19 -0
- package/src/snapshotGenerator.ts +190 -0
- package/src/superjson.ts +11 -0
- package/src/tests/aggregateRoot.spec.ts +532 -0
- package/src/tests/eventListener.spec.ts +57 -0
- package/src/tests/eventStore.spec.ts +86 -0
- package/src/tests/processManager.spec.ts +185 -0
- package/src/tests/projection.spec.ts +511 -0
- package/src/tests/projectionProcessManager.spec.ts +203 -0
- package/src/tests/setup.ts +230 -0
- package/src/tests/transaction.spec.ts +185 -0
- package/src/transaction.ts +93 -0
- package/src/utilityTypes.d.ts +110 -0
- package/src/utils.ts +43 -0
- package/src/wrappedEventBuilder.ts +181 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/lock.ts
|
|
2
|
+
import { Mutex } from "async-mutex";
|
|
3
|
+
function lock(defaultTtl = 5000) {
|
|
4
|
+
const locks = new Map;
|
|
5
|
+
return async (key) => {
|
|
6
|
+
const k = Array.isArray(key) ? key.join(":") : key;
|
|
7
|
+
if (!locks.has(k)) {
|
|
8
|
+
locks.set(k, new Mutex);
|
|
9
|
+
}
|
|
10
|
+
const mutex = locks.get(k);
|
|
11
|
+
const releaseMutex = await mutex.acquire();
|
|
12
|
+
let isReleased = false;
|
|
13
|
+
let timer;
|
|
14
|
+
const startTimer = (ttl) => {
|
|
15
|
+
timer = setTimeout(() => {
|
|
16
|
+
if (!isReleased) {
|
|
17
|
+
console.warn("lock ttl expired for key", k);
|
|
18
|
+
isReleased = true;
|
|
19
|
+
releaseMutex();
|
|
20
|
+
if (!mutex.isLocked()) {
|
|
21
|
+
locks.delete(k);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}, ttl);
|
|
25
|
+
};
|
|
26
|
+
startTimer(defaultTtl);
|
|
27
|
+
return {
|
|
28
|
+
async extend(ttl) {
|
|
29
|
+
if (isReleased) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
startTimer(ttl ?? defaultTtl / 2);
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
36
|
+
async release() {
|
|
37
|
+
if (isReleased) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
isReleased = true;
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
releaseMutex();
|
|
43
|
+
if (!mutex.isLocked()) {
|
|
44
|
+
locks.delete(k);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { lock };
|
|
52
|
+
|
|
53
|
+
//# debugId=1272FCF41F0B661064756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lock.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { Mutex } from 'async-mutex'\n\nexport default function lock(defaultTtl = 5000) {\n const locks = new Map<string, Mutex>()\n return async (key: string | string[]) => {\n const k = Array.isArray(key) ? key.join(':') : key\n if (!locks.has(k)) {\n locks.set(k, new Mutex())\n }\n\n const mutex = locks.get(k)!\n\n const releaseMutex = await mutex.acquire()\n let isReleased = false\n\n let timer: ReturnType<typeof setTimeout>\n const startTimer = (ttl: number) => {\n timer = setTimeout(() => {\n if (!isReleased) {\n console.warn('lock ttl expired for key', k)\n isReleased = true\n releaseMutex()\n if (!mutex.isLocked()) {\n locks.delete(k)\n }\n }\n }, ttl)\n }\n\n startTimer(defaultTtl)\n\n return {\n async extend(ttl?: number) {\n if (isReleased) {\n return false\n }\n clearTimeout(timer)\n startTimer(ttl ?? defaultTtl / 2)\n return true\n },\n async release() {\n if (isReleased) {\n return\n }\n\n isReleased = true\n clearTimeout(timer)\n releaseMutex()\n\n if (!mutex.isLocked()) {\n locks.delete(k)\n }\n },\n }\n }\n}\n\nexport type LockCreator = ReturnType<typeof lock>\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAAA;AAEA,SAAwB,IAAI,CAAC,aAAa,MAAM;AAAA,EAC9C,MAAM,QAAQ,IAAI;AAAA,EAClB,OAAO,OAAO,QAA2B;AAAA,IACvC,MAAM,IAAI,MAAM,QAAQ,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI;AAAA,IAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG;AAAA,MACjB,MAAM,IAAI,GAAG,IAAI,KAAO;AAAA,IAC1B;AAAA,IAEA,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,IAEzB,MAAM,eAAe,MAAM,MAAM,QAAQ;AAAA,IACzC,IAAI,aAAa;AAAA,IAEjB,IAAI;AAAA,IACJ,MAAM,aAAa,CAAC,QAAgB;AAAA,MAClC,QAAQ,WAAW,MAAM;AAAA,QACvB,IAAI,CAAC,YAAY;AAAA,UACf,QAAQ,KAAK,4BAA4B,CAAC;AAAA,UAC1C,aAAa;AAAA,UACb,aAAa;AAAA,UACb,IAAI,CAAC,MAAM,SAAS,GAAG;AAAA,YACrB,MAAM,OAAO,CAAC;AAAA,UAChB;AAAA,QACF;AAAA,SACC,GAAG;AAAA;AAAA,IAGR,WAAW,UAAU;AAAA,IAErB,OAAO;AAAA,WACC,OAAM,CAAC,KAAc;AAAA,QACzB,IAAI,YAAY;AAAA,UACd,OAAO;AAAA,QACT;AAAA,QACA,aAAa,KAAK;AAAA,QAClB,WAAW,OAAO,aAAa,CAAC;AAAA,QAChC,OAAO;AAAA;AAAA,WAEH,QAAO,GAAG;AAAA,QACd,IAAI,YAAY;AAAA,UACd;AAAA,QACF;AAAA,QAEA,aAAa;AAAA,QACb,aAAa,KAAK;AAAA,QAClB,aAAa;AAAA,QAEb,IAAI,CAAC,MAAM,SAAS,GAAG;AAAA,UACrB,MAAM,OAAO,CAAC;AAAA,QAChB;AAAA;AAAA,IAEJ;AAAA;AAAA;",
|
|
8
|
+
"debugId": "1272FCF41F0B661064756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createEventListener
|
|
3
|
+
} from "./chunk-24xjphpk.js";
|
|
4
|
+
import {
|
|
5
|
+
lock
|
|
6
|
+
} from "./chunk-ty9bhdvx.js";
|
|
7
|
+
import {
|
|
8
|
+
createTransaction
|
|
9
|
+
} from "./chunk-gq80tgvf.js";
|
|
10
|
+
import {
|
|
11
|
+
unwrapEvent,
|
|
12
|
+
wrappedEventBuilder
|
|
13
|
+
} from "./chunk-z388cd25.js";
|
|
14
|
+
import {
|
|
15
|
+
buildProjectionCreator
|
|
16
|
+
} from "./chunk-ncpk70p4.js";
|
|
17
|
+
import {
|
|
18
|
+
buildProcessManagerCreator
|
|
19
|
+
} from "./chunk-m07vf8q9.js";
|
|
20
|
+
import {
|
|
21
|
+
withRefreshing
|
|
22
|
+
} from "./chunk-9psg50cb.js";
|
|
23
|
+
|
|
24
|
+
// src/createEventStore.ts
|
|
25
|
+
function createEventStore(params) {
|
|
26
|
+
const transaction = createTransaction();
|
|
27
|
+
const { promise: initialized, resolve: resolveInitialized } = Promise.withResolvers();
|
|
28
|
+
const projections = new Map;
|
|
29
|
+
const processManagers = new Map;
|
|
30
|
+
const eventListeners = new Map;
|
|
31
|
+
const aggregatRootsByEvents = new Map;
|
|
32
|
+
let allReplaying = false;
|
|
33
|
+
let initializing = false;
|
|
34
|
+
const assertInitializing = () => {
|
|
35
|
+
if (!initializing && params.autoInit === false) {
|
|
36
|
+
throw new Error("Cannot emit events because the event store is not initialized. Set autoInit to true, or explicitly call .init() before emitting events.");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
let exclusiveEmitEventsChain = Promise.resolve();
|
|
40
|
+
const emitEvents = async (events) => {
|
|
41
|
+
exclusiveEmitEventsChain = exclusiveEmitEventsChain.then(async () => {
|
|
42
|
+
for (const event of events) {
|
|
43
|
+
await Promise.all([
|
|
44
|
+
...processManagers.values(),
|
|
45
|
+
...projections.values(),
|
|
46
|
+
...eventListeners.values()
|
|
47
|
+
].map((listener) => listener.apply(event)));
|
|
48
|
+
}
|
|
49
|
+
for (const event of events) {
|
|
50
|
+
await Promise.all([...processManagers.values(), ...eventListeners.values()].map((processManager) => processManager.applyAfter(event)));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return exclusiveEmitEventsChain;
|
|
54
|
+
};
|
|
55
|
+
const loadEvents = (select, range) => {
|
|
56
|
+
assertInitializing();
|
|
57
|
+
return params.loadEvents(select, range);
|
|
58
|
+
};
|
|
59
|
+
let exclusiveAppendEventsChain = initialized;
|
|
60
|
+
const appendEvents = async (streamId, events, expectedVersion) => {
|
|
61
|
+
assertInitializing();
|
|
62
|
+
exclusiveAppendEventsChain = exclusiveAppendEventsChain.then(async () => {
|
|
63
|
+
if (events.length === 0) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
67
|
+
const processedEvents = events.map((rawEvent) => {
|
|
68
|
+
const event = structuredClone(rawEvent);
|
|
69
|
+
if (params.postProcessEvent) {
|
|
70
|
+
const wrappedEvent = wrappedEventBuilder(event);
|
|
71
|
+
const { aggregateRoot } = aggregatRootsByEvents.get(event.type);
|
|
72
|
+
const processedEvent = unwrapEvent(params.postProcessEvent(wrappedEvent, aggregateRoot));
|
|
73
|
+
Object.assign(event, processedEvent);
|
|
74
|
+
}
|
|
75
|
+
return event;
|
|
76
|
+
});
|
|
77
|
+
return params.appendEvents(streamId, processedEvents, expectedVersion);
|
|
78
|
+
});
|
|
79
|
+
return exclusiveAppendEventsChain;
|
|
80
|
+
};
|
|
81
|
+
for (const aggregateRoot of params.aggregateRoots) {
|
|
82
|
+
aggregateRoot.eventTypes.forEach((eventType) => {
|
|
83
|
+
aggregatRootsByEvents.set(eventType, {
|
|
84
|
+
aggregateRoot,
|
|
85
|
+
eventType
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
aggregateRoot.linkEventStore(loadEvents, appendEvents, emitEvents, transaction, params.lock ?? lock(5000), params.aggregateRootSnapshot);
|
|
89
|
+
}
|
|
90
|
+
const baseCreateProjection = buildProjectionCreator({
|
|
91
|
+
loadEvents,
|
|
92
|
+
checkpoint: params.checkpoint,
|
|
93
|
+
snapshot: params.projectionSnapshot,
|
|
94
|
+
onReplay: params.onProjectionReplay
|
|
95
|
+
});
|
|
96
|
+
const baseCreateProcessManager = buildProcessManagerCreator({
|
|
97
|
+
loadEvents,
|
|
98
|
+
checkpoint: params.checkpoint,
|
|
99
|
+
onRefresh: params.onProcessManagerRefresh
|
|
100
|
+
});
|
|
101
|
+
const getRelatedStreamEvents = (handlers) => {
|
|
102
|
+
const relatedStreamEvents = new Map;
|
|
103
|
+
for (const handlerName of Object.keys(handlers)) {
|
|
104
|
+
const sanitizedHandlerName = handlerName.replace(/^(on|after)/, "");
|
|
105
|
+
if (!aggregatRootsByEvents.has(sanitizedHandlerName)) {
|
|
106
|
+
throw new Error(`event handler ${handlerName} has no corresponding event in any aggregate root`);
|
|
107
|
+
}
|
|
108
|
+
const {
|
|
109
|
+
aggregateRoot: { type: aggregateRootName },
|
|
110
|
+
eventType
|
|
111
|
+
} = aggregatRootsByEvents.get(sanitizedHandlerName);
|
|
112
|
+
if (!relatedStreamEvents.has(aggregateRootName)) {
|
|
113
|
+
relatedStreamEvents.set(aggregateRootName, new Set);
|
|
114
|
+
}
|
|
115
|
+
relatedStreamEvents.get(aggregateRootName).add(eventType);
|
|
116
|
+
}
|
|
117
|
+
return Array.from(relatedStreamEvents, ([stream, events]) => ({
|
|
118
|
+
stream,
|
|
119
|
+
events: Array.from(events)
|
|
120
|
+
}));
|
|
121
|
+
};
|
|
122
|
+
const initialize = async () => {
|
|
123
|
+
initializing = true;
|
|
124
|
+
await Promise.all(projections.values().map((projection) => projection.init(true)));
|
|
125
|
+
await Promise.all(processManagers.values().map((processManager) => processManager.init(true)));
|
|
126
|
+
resolveInitialized();
|
|
127
|
+
};
|
|
128
|
+
setImmediate(() => {
|
|
129
|
+
if (allReplaying || params.autoInit === false) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
initialize();
|
|
133
|
+
});
|
|
134
|
+
const api = {
|
|
135
|
+
createProjection(name) {
|
|
136
|
+
if (projections.has(name)) {
|
|
137
|
+
throw new Error(`Projection ${name} is already registered`);
|
|
138
|
+
}
|
|
139
|
+
const api2 = baseCreateProjection(name);
|
|
140
|
+
const internalApi = api2;
|
|
141
|
+
internalApi.setRelatedStreamEvents((handlers) => getRelatedStreamEvents(handlers));
|
|
142
|
+
projections.set(name, internalApi);
|
|
143
|
+
if (initializing) {
|
|
144
|
+
internalApi.init(true);
|
|
145
|
+
}
|
|
146
|
+
return api2;
|
|
147
|
+
},
|
|
148
|
+
createProcessManager(name) {
|
|
149
|
+
if (processManagers.has(name)) {
|
|
150
|
+
throw new Error(`Process Manager ${name} is already registered`);
|
|
151
|
+
}
|
|
152
|
+
const api2 = baseCreateProcessManager(name);
|
|
153
|
+
const internalApi = api2;
|
|
154
|
+
internalApi.setRelatedStreamEvents((handlers) => getRelatedStreamEvents(handlers));
|
|
155
|
+
processManagers.set(name, internalApi);
|
|
156
|
+
if (initializing) {
|
|
157
|
+
internalApi.init(true);
|
|
158
|
+
}
|
|
159
|
+
return api2;
|
|
160
|
+
},
|
|
161
|
+
createEventListener(name) {
|
|
162
|
+
if (eventListeners.has(name)) {
|
|
163
|
+
throw new Error(`Event Listener ${name} is already registered`);
|
|
164
|
+
}
|
|
165
|
+
const api2 = createEventListener(name);
|
|
166
|
+
const internalApi = api2;
|
|
167
|
+
eventListeners.set(name, internalApi);
|
|
168
|
+
return api2;
|
|
169
|
+
},
|
|
170
|
+
async isReady() {
|
|
171
|
+
await initialized;
|
|
172
|
+
await Promise.all([
|
|
173
|
+
...projections.values().map((projection) => projection.isReady()),
|
|
174
|
+
...processManagers.values().map((projection) => projection.isReady())
|
|
175
|
+
]);
|
|
176
|
+
},
|
|
177
|
+
async rebuild() {
|
|
178
|
+
allReplaying = true;
|
|
179
|
+
initializing = true;
|
|
180
|
+
const replayProjections = Array.from(projections.values()).filter((projection) => projection.canReplay());
|
|
181
|
+
const replayProcessManagers = Array.from(processManagers.values()).filter((processManager) => processManager.hasState());
|
|
182
|
+
await Promise.all([
|
|
183
|
+
...replayProjections.map((projection) => projection.init(false)),
|
|
184
|
+
...replayProcessManagers.map((projection) => projection.init(false)),
|
|
185
|
+
...replayProjections.map((projection) => projection.beginReplay()),
|
|
186
|
+
...replayProcessManagers.map((processManager) => processManager.beginRefresh())
|
|
187
|
+
]);
|
|
188
|
+
await withRefreshing(async () => {
|
|
189
|
+
let index = 0;
|
|
190
|
+
for await (const event of loadEvents(null)) {
|
|
191
|
+
index++;
|
|
192
|
+
await Promise.all([
|
|
193
|
+
...replayProjections.map((projection) => projection.progressReplay(index, event)),
|
|
194
|
+
...replayProcessManagers.map((processManager) => processManager.progressRefresh(index, event, false)),
|
|
195
|
+
...Array.from(eventListeners.values(), (listener) => listener.apply(event))
|
|
196
|
+
]);
|
|
197
|
+
await Promise.all([
|
|
198
|
+
...replayProcessManagers.map((processManager) => processManager.progressRefresh(index, event, true)),
|
|
199
|
+
...Array.from(eventListeners.values(), (listener) => listener.applyAfter(event))
|
|
200
|
+
]);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
await Promise.all([
|
|
204
|
+
...replayProjections.map((projection) => projection.endReplay()),
|
|
205
|
+
...replayProcessManagers.map((processManager) => processManager.endRefresh())
|
|
206
|
+
]);
|
|
207
|
+
await Promise.all(projections.values().map(async (projection) => {
|
|
208
|
+
if (!projection.canReplay()) {
|
|
209
|
+
await projection.init(true);
|
|
210
|
+
}
|
|
211
|
+
}));
|
|
212
|
+
resolveInitialized();
|
|
213
|
+
},
|
|
214
|
+
async init() {
|
|
215
|
+
if (initializing) {
|
|
216
|
+
return api;
|
|
217
|
+
}
|
|
218
|
+
await initialize();
|
|
219
|
+
return api;
|
|
220
|
+
},
|
|
221
|
+
getAggregateRoot(type) {
|
|
222
|
+
return params.aggregateRoots.find((aggregateRoot) => aggregateRoot.type === type);
|
|
223
|
+
},
|
|
224
|
+
async transaction(handler) {
|
|
225
|
+
return transaction.run(handler);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
return api;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { createEventStore };
|
|
232
|
+
|
|
233
|
+
//# debugId=7493A3991CE6299464756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/createEventStore.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n AggregateRootApi,\n AggregateRootSnapshotMethods,\n AnyAggregateRoot,\n InternalAggregateRootApi,\n} from './createAggregateRoot.ts'\nimport type { CheckpointMethods } from './createCheckpointApi.ts'\nimport {\n type EventListenerApi,\n type InternalEventListener,\n createEventListener,\n} from './createEventListener.ts'\nimport {\n type InternalProcessManager,\n type ProcessManagerApi,\n buildProcessManagerCreator,\n} from './createProcessManager.ts'\nimport {\n type InternalProjection,\n type ProjectionApi,\n type ProjectionApiWithSnapshots,\n type ProjectionSnapshotMethods,\n buildProjectionCreator,\n} from './createProjection.ts'\nimport lock, { type LockCreator } from './lock.ts'\nimport { withRefreshing } from './refresh.ts'\nimport createTransaction from './transaction.ts'\nimport type {\n BaseInputEvent,\n BaseOutputEvent,\n EventHandlers,\n EventTemplate,\n MaybePromise,\n Stream,\n StreamEvents,\n StreamId,\n StreamIdEvents,\n} from './utilityTypes.js'\nimport {\n type WrappedEvent,\n unwrapEvent,\n wrappedEventBuilder,\n} from './wrappedEventBuilder.ts'\n\nexport type LoadEvents = (\n select:\n | Stream\n | StreamId\n | Stream[]\n | StreamId[]\n | StreamEvents\n | StreamEvents[]\n | StreamIdEvents\n | StreamIdEvents[]\n | null,\n range?: { from: number; to?: number },\n) => AsyncGenerator<BaseOutputEvent>\n\ntype AppendEvents = (\n streamId: StreamId,\n events: BaseInputEvent[],\n expectedVersion: number,\n) => MaybePromise<BaseOutputEvent[]>\n\nexport type InternalAppendEvents = (\n streamId: StreamId,\n events: BaseInputEvent[],\n expectedVersion: number,\n) => MaybePromise<BaseOutputEvent[]>\n\nexport type EmitEvents = (events: BaseOutputEvent[]) => Promise<void>\n\nexport type EventsFromRoot<Root> =\n Root extends Omit<AggregateRootApi<any, any, infer E>, any> ? E : never\n\nexport type OnProgress = {\n begin?: (projectionName: string) => void\n progress?: (\n projectionName: string,\n index: number,\n event: BaseOutputEvent,\n ) => void\n end?: (projectionName: string) => void\n}\n\ninterface BaseEventStoreParams<Root extends AnyAggregateRoot> {\n loadEvents: LoadEvents\n appendEvents: AppendEvents\n aggregateRoots: Root[]\n checkpoint: CheckpointMethods\n lock?: LockCreator\n projectionSnapshot?: ProjectionSnapshotMethods\n onProjectionReplay?: OnProgress\n onProcessManagerRefresh?: OnProgress\n autoInit?: boolean\n}\n\nexport interface EventStoreParamsWithAggregateRootSnapshots<\n Root extends AnyAggregateRoot,\n> extends BaseEventStoreParams<Root> {\n aggregateRootSnapshot: AggregateRootSnapshotMethods\n postProcessEvent?: (\n event: WrappedEvent<EventsFromRoot<Root>>,\n aggregateRoot: Root,\n ) => WrappedEvent\n}\n\nexport interface EventStoreParamsWithoutAggregateRootSnapshots<\n Root extends AnyAggregateRoot,\n> extends BaseEventStoreParams<Root> {\n aggregateRoots: Root[]\n aggregateRootSnapshot?: never\n postProcessEvent?: (\n event: WrappedEvent<EventsFromRoot<Root>>,\n aggregateRoot: Root,\n ) => WrappedEvent\n}\n\nexport type EventStore<\n Event extends EventTemplate,\n Root extends AnyAggregateRoot,\n SupportsProjectionSnapshots = boolean,\n> = {\n createProjection(\n name: string,\n ): SupportsProjectionSnapshots extends true\n ? ProjectionApiWithSnapshots<Event>\n : ProjectionApi<Event>\n createProcessManager(name: string): ProcessManagerApi<never, Event>\n createEventListener(name: string): EventListenerApi<Event>\n rebuild: () => Promise<void>\n isReady: () => Promise<void>\n init: () => Promise<EventStore<Event, Root, SupportsProjectionSnapshots>>\n getAggregateRoot<S extends Root['type']>(\n type: S,\n ): Root extends { type: S } ? Root : never\n transaction<T extends () => any>(handler: T): Promise<Awaited<ReturnType<T>>>\n}\n\nexport function createEventStore<const Root extends AnyAggregateRoot>(\n params: EventStoreParamsWithoutAggregateRootSnapshots<Root>,\n): EventStore<EventsFromRoot<Root>, Root, false>\nexport function createEventStore<const Root extends AnyAggregateRoot>(\n params: EventStoreParamsWithAggregateRootSnapshots<Root>,\n): EventStore<EventsFromRoot<Root>, Root, true>\nexport function createEventStore<const Root extends AnyAggregateRoot>(\n params:\n | EventStoreParamsWithoutAggregateRootSnapshots<Root>\n | EventStoreParamsWithAggregateRootSnapshots<Root>,\n) {\n const transaction = createTransaction()\n const { promise: initialized, resolve: resolveInitialized } =\n Promise.withResolvers<void>()\n const projections = new Map<string, InternalProjection>()\n const processManagers = new Map<string, InternalProcessManager>()\n const eventListeners = new Map<string, InternalEventListener>()\n const aggregatRootsByEvents = new Map<\n string,\n { aggregateRoot: InternalAggregateRootApi; eventType: string }\n >()\n\n let allReplaying = false\n let initializing = false\n\n const assertInitializing = () => {\n if (!initializing && params.autoInit === false) {\n throw new Error(\n 'Cannot emit events because the event store is not initialized. Set autoInit to true, or explicitly call .init() before emitting events.',\n )\n }\n }\n\n let exclusiveEmitEventsChain: Promise<any> = Promise.resolve()\n const emitEvents: EmitEvents = async (events) => {\n exclusiveEmitEventsChain = exclusiveEmitEventsChain.then(async () => {\n for (const event of events) {\n await Promise.all(\n [\n ...processManagers.values(),\n ...projections.values(),\n ...eventListeners.values(),\n ].map((listener) => listener.apply(event)),\n )\n }\n\n for (const event of events) {\n await Promise.all(\n [...processManagers.values(), ...eventListeners.values()].map(\n (processManager) => processManager.applyAfter(event),\n ),\n )\n }\n })\n\n return exclusiveEmitEventsChain\n }\n\n const loadEvents: LoadEvents = (select, range) => {\n assertInitializing()\n return params.loadEvents(select, range)\n }\n\n let exclusiveAppendEventsChain: Promise<any> = initialized\n const appendEvents: InternalAppendEvents = async (\n streamId,\n events,\n expectedVersion,\n ) => {\n assertInitializing()\n exclusiveAppendEventsChain = exclusiveAppendEventsChain.then(async () => {\n if (events.length === 0) {\n return []\n }\n await new Promise((resolve) => setImmediate(resolve)) // edge case for sync setup, see 'can execute after effect' test\n\n const processedEvents = events.map((rawEvent) => {\n const event = structuredClone(rawEvent)\n if (params.postProcessEvent) {\n const wrappedEvent = wrappedEventBuilder(event) as WrappedEvent<any>\n const { aggregateRoot } = aggregatRootsByEvents.get(event.type)!\n const processedEvent = unwrapEvent(\n params.postProcessEvent(\n wrappedEvent,\n aggregateRoot as unknown as Root,\n ),\n )\n Object.assign(event, processedEvent)\n }\n\n return event\n })\n\n return params.appendEvents(streamId, processedEvents, expectedVersion)\n })\n\n return exclusiveAppendEventsChain\n }\n\n for (const aggregateRoot of params.aggregateRoots as unknown as InternalAggregateRootApi[]) {\n aggregateRoot.eventTypes.forEach((eventType) => {\n aggregatRootsByEvents.set(eventType, {\n aggregateRoot,\n eventType,\n })\n })\n\n aggregateRoot.linkEventStore(\n loadEvents,\n appendEvents,\n emitEvents,\n transaction,\n params.lock ?? lock(5000),\n params.aggregateRootSnapshot,\n )\n }\n\n const baseCreateProjection = buildProjectionCreator({\n loadEvents,\n checkpoint: params.checkpoint,\n snapshot: params.projectionSnapshot,\n onReplay: params.onProjectionReplay,\n })\n\n const baseCreateProcessManager = buildProcessManagerCreator({\n loadEvents,\n checkpoint: params.checkpoint,\n onRefresh: params.onProcessManagerRefresh,\n })\n\n const getRelatedStreamEvents = (\n handlers: EventHandlers<any>,\n ): StreamEvents[] => {\n const relatedStreamEvents = new Map<string, Set<string>>()\n for (const handlerName of Object.keys(handlers)) {\n const sanitizedHandlerName = handlerName.replace(/^(on|after)/, '')\n if (!aggregatRootsByEvents.has(sanitizedHandlerName)) {\n throw new Error(\n `event handler ${handlerName} has no corresponding event in any aggregate root`,\n )\n }\n const {\n aggregateRoot: { type: aggregateRootName },\n eventType,\n } = aggregatRootsByEvents.get(sanitizedHandlerName)!\n if (!relatedStreamEvents.has(aggregateRootName)) {\n relatedStreamEvents.set(aggregateRootName, new Set())\n }\n relatedStreamEvents.get(aggregateRootName)!.add(eventType)\n }\n\n return Array.from(relatedStreamEvents, ([stream, events]) => ({\n stream,\n events: Array.from(events),\n }))\n }\n\n const initialize = async () => {\n initializing = true\n await Promise.all(\n projections.values().map((projection) => projection.init(true)),\n )\n await Promise.all(\n processManagers\n .values()\n .map((processManager) => processManager.init(true)),\n )\n resolveInitialized()\n }\n setImmediate(() => {\n if (allReplaying || params.autoInit === false) {\n return\n }\n\n initialize()\n })\n\n const api = {\n createProjection(name) {\n if (projections.has(name)) {\n throw new Error(`Projection ${name} is already registered`)\n }\n const api = baseCreateProjection(name)\n const internalApi = api as unknown as InternalProjection\n internalApi.setRelatedStreamEvents((handlers) =>\n getRelatedStreamEvents(handlers),\n )\n projections.set(name, internalApi)\n if (initializing) {\n internalApi.init(true)\n }\n return api\n },\n createProcessManager(name) {\n if (processManagers.has(name)) {\n throw new Error(`Process Manager ${name} is already registered`)\n }\n const api = baseCreateProcessManager(name)\n\n const internalApi = api as unknown as InternalProcessManager\n internalApi.setRelatedStreamEvents((handlers) =>\n getRelatedStreamEvents(handlers),\n )\n processManagers.set(name, internalApi)\n if (initializing) {\n internalApi.init(true)\n }\n return api\n },\n createEventListener(name) {\n if (eventListeners.has(name)) {\n throw new Error(`Event Listener ${name} is already registered`)\n }\n\n const api = createEventListener(name)\n\n const internalApi = api as unknown as InternalEventListener\n eventListeners.set(name, internalApi)\n return api\n },\n async isReady() {\n await initialized\n await Promise.all([\n ...projections.values().map((projection) => projection.isReady()),\n ...processManagers.values().map((projection) => projection.isReady()),\n ])\n },\n async rebuild() {\n allReplaying = true\n initializing = true\n const replayProjections = Array.from(projections.values()).filter(\n (projection) => projection.canReplay(),\n )\n const replayProcessManagers = Array.from(processManagers.values()).filter(\n (processManager) => processManager.hasState(),\n )\n\n await Promise.all([\n ...replayProjections.map((projection) => projection.init(false)),\n ...replayProcessManagers.map((projection) => projection.init(false)),\n ...replayProjections.map((projection) => projection.beginReplay()),\n ...replayProcessManagers.map((processManager) =>\n processManager.beginRefresh(),\n ),\n ])\n\n await withRefreshing(async () => {\n let index = 0\n for await (const event of loadEvents(null)) {\n index++\n await Promise.all([\n ...replayProjections.map((projection) =>\n projection.progressReplay(index, event),\n ),\n ...replayProcessManagers.map((processManager) =>\n processManager.progressRefresh(index, event, false),\n ),\n ...Array.from(eventListeners.values(), (listener) =>\n listener.apply(event),\n ),\n ])\n await Promise.all([\n ...replayProcessManagers.map((processManager) =>\n processManager.progressRefresh(index, event, true),\n ),\n ...Array.from(eventListeners.values(), (listener) =>\n listener.applyAfter(event),\n ),\n ])\n }\n })\n\n await Promise.all([\n ...replayProjections.map((projection) => projection.endReplay()),\n ...replayProcessManagers.map((processManager) =>\n processManager.endRefresh(),\n ),\n ])\n\n await Promise.all(\n projections.values().map(async (projection) => {\n if (!projection.canReplay()) {\n await projection.init(true)\n }\n }),\n )\n resolveInitialized()\n },\n async init() {\n if (initializing) {\n return api\n }\n await initialize()\n return api\n },\n getAggregateRoot(type) {\n return params.aggregateRoots.find(\n (aggregateRoot) => aggregateRoot.type === type,\n ) as any\n },\n async transaction(handler) {\n return transaction.run(handler)\n },\n } as EventStore<any, Root>\n\n return api\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7493A3991CE6299464756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReplaySkipError
|
|
3
|
+
} from "./chunk-qdczt68t.js";
|
|
4
|
+
|
|
5
|
+
// src/replay.ts
|
|
6
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
+
var replayStore = new AsyncLocalStorage;
|
|
8
|
+
function isReplaying() {
|
|
9
|
+
return replayStore.getStore() ?? false;
|
|
10
|
+
}
|
|
11
|
+
function withReplaying(handler) {
|
|
12
|
+
return replayStore.run(true, handler);
|
|
13
|
+
}
|
|
14
|
+
function skipReplay() {
|
|
15
|
+
if (isReplaying()) {
|
|
16
|
+
throw new ReplaySkipError;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { isReplaying, withReplaying, skipReplay };
|
|
21
|
+
|
|
22
|
+
//# debugId=54300463139EC0AC64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/replay.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport { ReplaySkipError } from './errors.ts'\n\nconst replayStore = new AsyncLocalStorage<boolean>()\n\nexport function isReplaying() {\n return replayStore.getStore() ?? false\n}\n\nexport function withReplaying<T extends () => any>(handler: T) {\n return replayStore.run(true, handler)\n}\n\nexport function skipReplay() {\n if (isReplaying()) {\n throw new ReplaySkipError()\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;AAAA;AAIA,IAAM,cAAc,IAAI;AAEjB,SAAS,WAAW,GAAG;AAAA,EAC5B,OAAO,YAAY,SAAS,KAAK;AAAA;AAG5B,SAAS,aAAkC,CAAC,SAAY;AAAA,EAC7D,OAAO,YAAY,IAAI,MAAM,OAAO;AAAA;AAG/B,SAAS,UAAU,GAAG;AAAA,EAC3B,IAAI,YAAY,GAAG;AAAA,IACjB,MAAM,IAAI;AAAA,EACZ;AAAA;",
|
|
8
|
+
"debugId": "54300463139EC0AC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/wrappedEventBuilder.ts
|
|
2
|
+
var eventInternalsSymbol = Symbol.for("event-internals");
|
|
3
|
+
function getInternals(event) {
|
|
4
|
+
return event[eventInternalsSymbol];
|
|
5
|
+
}
|
|
6
|
+
function unwrapEvent(event) {
|
|
7
|
+
return getInternals(event).event;
|
|
8
|
+
}
|
|
9
|
+
async function handleEventError(event, error) {
|
|
10
|
+
await getInternals(event).errorHandler?.(error);
|
|
11
|
+
}
|
|
12
|
+
function wrappedEventBuilder(type, payload) {
|
|
13
|
+
let errorHandler;
|
|
14
|
+
const event = typeof type === "string" ? {
|
|
15
|
+
type,
|
|
16
|
+
payload: payload ?? null
|
|
17
|
+
} : type;
|
|
18
|
+
return {
|
|
19
|
+
withCausationId(causationId) {
|
|
20
|
+
if (causationId) {
|
|
21
|
+
event.causationId = causationId;
|
|
22
|
+
}
|
|
23
|
+
return this;
|
|
24
|
+
},
|
|
25
|
+
withDefaultCausationId(causationId) {
|
|
26
|
+
if (causationId && !event.causationId) {
|
|
27
|
+
event.causationId = causationId;
|
|
28
|
+
}
|
|
29
|
+
return this;
|
|
30
|
+
},
|
|
31
|
+
withCorrelationId(correlationId) {
|
|
32
|
+
if (correlationId) {
|
|
33
|
+
event.correlationId = correlationId;
|
|
34
|
+
}
|
|
35
|
+
return this;
|
|
36
|
+
},
|
|
37
|
+
withDefaultCorrelationId(correlationId) {
|
|
38
|
+
if (correlationId && !event.correlationId) {
|
|
39
|
+
event.correlationId = correlationId;
|
|
40
|
+
}
|
|
41
|
+
return this;
|
|
42
|
+
},
|
|
43
|
+
withActorId(actorId) {
|
|
44
|
+
if (actorId) {
|
|
45
|
+
event.actorId = actorId;
|
|
46
|
+
}
|
|
47
|
+
return this;
|
|
48
|
+
},
|
|
49
|
+
withDefaultActorId(actorId) {
|
|
50
|
+
if (actorId && !event.actorId) {
|
|
51
|
+
event.actorId = actorId;
|
|
52
|
+
}
|
|
53
|
+
return this;
|
|
54
|
+
},
|
|
55
|
+
withMetadata(metadata) {
|
|
56
|
+
if (metadata) {
|
|
57
|
+
event.metadata = metadata;
|
|
58
|
+
}
|
|
59
|
+
return this;
|
|
60
|
+
},
|
|
61
|
+
withDefaultMetadata(metadata) {
|
|
62
|
+
if (metadata && !event.metadata) {
|
|
63
|
+
event.metadata = metadata;
|
|
64
|
+
}
|
|
65
|
+
return this;
|
|
66
|
+
},
|
|
67
|
+
withAdditionalMetadata(metadata) {
|
|
68
|
+
if (!metadata) {
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
if (!event.metadata) {
|
|
72
|
+
event.metadata = metadata;
|
|
73
|
+
} else {
|
|
74
|
+
event.metadata = Object.assign(event.metadata, metadata);
|
|
75
|
+
}
|
|
76
|
+
return this;
|
|
77
|
+
},
|
|
78
|
+
onValidationError(handler) {
|
|
79
|
+
errorHandler = handler;
|
|
80
|
+
return this;
|
|
81
|
+
},
|
|
82
|
+
get [eventInternalsSymbol]() {
|
|
83
|
+
return { event, errorHandler };
|
|
84
|
+
},
|
|
85
|
+
type
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { unwrapEvent, handleEventError, wrappedEventBuilder };
|
|
90
|
+
|
|
91
|
+
//# debugId=D64CAD086572911164756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/wrappedEventBuilder.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { z } from 'zod/v4'\n\nimport type { EventValidationError } from './errors.ts'\nimport type {\n BaseInputEvent,\n EventTemplate,\n Max,\n MaybePromise,\n} from './utilityTypes.js'\n\nexport type WrappedEvent<E extends EventTemplate = EventTemplate> = {\n withCausationId(causationId: string | null | undefined): WrappedEvent<E>\n withDefaultCausationId(\n causationId: string | null | undefined,\n ): WrappedEvent<E>\n withCorrelationId(correlationId: string | null | undefined): WrappedEvent<E>\n withDefaultCorrelationId(\n correlationId: string | null | undefined,\n ): WrappedEvent<E>\n withActorId(actorId: string | null | undefined): WrappedEvent<E>\n withDefaultActorId(actorId: string | null | undefined): WrappedEvent<E>\n withMetadata(\n metadata: Record<string, any> | null | undefined,\n ): WrappedEvent<E>\n withDefaultMetadata(\n metadata: Record<string, any> | null | undefined,\n ): WrappedEvent<E>\n withAdditionalMetadata(\n metadata: Record<string, any> | null | undefined,\n ): WrappedEvent<E>\n onValidationError(\n errorHandler: (error: EventValidationError) => MaybePromise<void>,\n ): WrappedEvent<E>\n readonly type: string\n}\n\nconst eventInternalsSymbol = Symbol.for('event-internals')\n\nfunction getInternals(event: WrappedEvent) {\n return (event as any)[eventInternalsSymbol] as {\n event: Omit<BaseInputEvent, 'schemaVersion'>\n errorHandler?: (error: EventValidationError) => MaybePromise<void>\n }\n}\n\nexport function unwrapEvent<E extends EventTemplate = EventTemplate>(\n event: WrappedEvent<E>,\n) {\n return getInternals(event).event as {\n [K in E as K['type']]: Omit<\n BaseInputEvent<K['type'], z.output<K['schema']>>,\n 'schemaVersion'\n >\n }[E['type']]\n}\n\nexport async function handleEventError(\n event: WrappedEvent,\n error: EventValidationError,\n) {\n await getInternals(event).errorHandler?.(error)\n}\n\ntype LatestSchema<\n Event extends EventTemplate,\n LatestVersion = Max<Event['version']>,\n Schema = Event extends { version: LatestVersion } ? Event['schema'] : never,\n> = LatestVersion extends number ? z.input<Schema> : never\n\ntype EventMap<T extends EventTemplate> = {\n [K in T as K['type']]: LatestSchema<K>\n}\n\nexport type WrappedEventBuilder<Event extends EventTemplate> = {\n <\n Type extends Event['type'],\n LatestVersion = Max<\n Event extends { type: Type } ? Event['version'] : never\n >,\n Schema = Event extends { type: Type; version: LatestVersion }\n ? Event['schema']\n : never,\n >(\n ...args: z.input<Schema> extends null\n ? [type: Type, payload?: null]\n : [type: Type, payload: z.input<Schema>]\n ): WrappedEvent\n $inferPayload: EventMap<Event>\n}\n\nexport function wrappedEventBuilder(\n type: string,\n payload: unknown,\n): WrappedEvent\nexport function wrappedEventBuilder(event: BaseInputEvent): WrappedEvent\nexport function wrappedEventBuilder(\n type: string | BaseInputEvent,\n payload?: unknown,\n): WrappedEvent {\n let errorHandler: (error: EventValidationError) => MaybePromise<void>\n const event: Omit<BaseInputEvent, 'schemaVersion'> =\n typeof type === 'string'\n ? {\n type,\n payload: payload ?? null,\n }\n : type\n\n return {\n withCausationId(causationId) {\n if (causationId) {\n event.causationId = causationId\n }\n return this\n },\n withDefaultCausationId(causationId) {\n if (causationId && !event.causationId) {\n event.causationId = causationId\n }\n return this\n },\n withCorrelationId(correlationId) {\n if (correlationId) {\n event.correlationId = correlationId\n }\n return this\n },\n withDefaultCorrelationId(correlationId) {\n if (correlationId && !event.correlationId) {\n event.correlationId = correlationId\n }\n return this\n },\n withActorId(actorId) {\n if (actorId) {\n event.actorId = actorId\n }\n return this\n },\n withDefaultActorId(actorId) {\n if (actorId && !event.actorId) {\n event.actorId = actorId\n }\n return this\n },\n withMetadata(metadata) {\n if (metadata) {\n event.metadata = metadata\n }\n return this\n },\n withDefaultMetadata(metadata) {\n if (metadata && !event.metadata) {\n event.metadata = metadata\n }\n return this\n },\n withAdditionalMetadata(metadata) {\n if (!metadata) {\n return this\n }\n if (!event.metadata) {\n event.metadata = metadata\n } else {\n event.metadata = Object.assign(event.metadata, metadata)\n }\n\n return this\n },\n onValidationError(handler) {\n errorHandler = handler\n return this\n },\n\n get [eventInternalsSymbol]() {\n return { event, errorHandler }\n },\n\n type,\n } as WrappedEvent\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAoCA,IAAM,uBAAuB,OAAO,IAAI,iBAAiB;AAEzD,SAAS,YAAY,CAAC,OAAqB;AAAA,EACzC,OAAQ,MAAc;AAAA;AAMjB,SAAS,WAAoD,CAClE,OACA;AAAA,EACA,OAAO,aAAa,KAAK,EAAE;AAAA;AAQ7B,eAAsB,gBAAgB,CACpC,OACA,OACA;AAAA,EACA,MAAM,aAAa,KAAK,EAAE,eAAe,KAAK;AAAA;AAmCzC,SAAS,mBAAmB,CACjC,MACA,SACc;AAAA,EACd,IAAI;AAAA,EACJ,MAAM,QACJ,OAAO,SAAS,WACZ;AAAA,IACE;AAAA,IACA,SAAS,WAAW;AAAA,EACtB,IACA;AAAA,EAEN,OAAO;AAAA,IACL,eAAe,CAAC,aAAa;AAAA,MAC3B,IAAI,aAAa;AAAA,QACf,MAAM,cAAc;AAAA,MACtB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,sBAAsB,CAAC,aAAa;AAAA,MAClC,IAAI,eAAe,CAAC,MAAM,aAAa;AAAA,QACrC,MAAM,cAAc;AAAA,MACtB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,iBAAiB,CAAC,eAAe;AAAA,MAC/B,IAAI,eAAe;AAAA,QACjB,MAAM,gBAAgB;AAAA,MACxB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,wBAAwB,CAAC,eAAe;AAAA,MACtC,IAAI,iBAAiB,CAAC,MAAM,eAAe;AAAA,QACzC,MAAM,gBAAgB;AAAA,MACxB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,WAAW,CAAC,SAAS;AAAA,MACnB,IAAI,SAAS;AAAA,QACX,MAAM,UAAU;AAAA,MAClB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,kBAAkB,CAAC,SAAS;AAAA,MAC1B,IAAI,WAAW,CAAC,MAAM,SAAS;AAAA,QAC7B,MAAM,UAAU;AAAA,MAClB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,YAAY,CAAC,UAAU;AAAA,MACrB,IAAI,UAAU;AAAA,QACZ,MAAM,WAAW;AAAA,MACnB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,mBAAmB,CAAC,UAAU;AAAA,MAC5B,IAAI,YAAY,CAAC,MAAM,UAAU;AAAA,QAC/B,MAAM,WAAW;AAAA,MACnB;AAAA,MACA,OAAO;AAAA;AAAA,IAET,sBAAsB,CAAC,UAAU;AAAA,MAC/B,IAAI,CAAC,UAAU;AAAA,QACb,OAAO;AAAA,MACT;AAAA,MACA,IAAI,CAAC,MAAM,UAAU;AAAA,QACnB,MAAM,WAAW;AAAA,MACnB,EAAO;AAAA,QACL,MAAM,WAAW,OAAO,OAAO,MAAM,UAAU,QAAQ;AAAA;AAAA,MAGzD,OAAO;AAAA;AAAA,IAET,iBAAiB,CAAC,SAAS;AAAA,MACzB,eAAe;AAAA,MACf,OAAO;AAAA;AAAA,SAGJ,qBAAqB,GAAG;AAAA,MAC3B,OAAO,EAAE,OAAO,aAAa;AAAA;AAAA,IAG/B;AAAA,EACF;AAAA;",
|
|
8
|
+
"debugId": "D64CAD086572911164756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationError
|
|
3
|
+
} from "./chunk-qdczt68t.js";
|
|
4
|
+
|
|
5
|
+
// src/createCheckpointApi.ts
|
|
6
|
+
import z from "zod/v4";
|
|
7
|
+
var checkpointSchema = z.object({
|
|
8
|
+
lastEventPosition: z.int(),
|
|
9
|
+
type: z.enum(["projection", "processManager"]),
|
|
10
|
+
name: z.string(),
|
|
11
|
+
metadata: z.record(z.string(), z.any())
|
|
12
|
+
});
|
|
13
|
+
function createCheckpointApi(params) {
|
|
14
|
+
const api = {
|
|
15
|
+
relatedStreamEvents: params.relatedStreamEvents,
|
|
16
|
+
async clear() {
|
|
17
|
+
await params.delete(params.type, params.name);
|
|
18
|
+
},
|
|
19
|
+
async upsert(event, metadata = {}) {
|
|
20
|
+
await params.upsert({
|
|
21
|
+
lastEventPosition: event.position,
|
|
22
|
+
type: params.type,
|
|
23
|
+
name: params.name,
|
|
24
|
+
metadata
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
async rehydrated(options = {}) {
|
|
28
|
+
const checkpoint = await params.get(params.type, params.name);
|
|
29
|
+
let startPosition = 0;
|
|
30
|
+
if (checkpoint) {
|
|
31
|
+
const result = checkpointSchema.safeParse(checkpoint);
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
throw new ValidationError(`loaded checkpoint for ${params.type} ${params.name} does not comply with schema`, result.error);
|
|
34
|
+
}
|
|
35
|
+
startPosition = checkpoint.lastEventPosition + 1;
|
|
36
|
+
options.onMetadata?.(checkpoint.metadata);
|
|
37
|
+
}
|
|
38
|
+
const events = params.loadEvents(params.relatedStreamEvents, {
|
|
39
|
+
from: startPosition
|
|
40
|
+
});
|
|
41
|
+
let index = 0;
|
|
42
|
+
let lastEvent = null;
|
|
43
|
+
for await (const event of events) {
|
|
44
|
+
lastEvent = event;
|
|
45
|
+
await params.apply(event);
|
|
46
|
+
index++;
|
|
47
|
+
options.onProgress?.(index, event);
|
|
48
|
+
}
|
|
49
|
+
if (!lastEvent) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (params.applyAfter) {
|
|
53
|
+
for await (const event of events) {
|
|
54
|
+
await params.applyAfter(event);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
await api.upsert(lastEvent, options.getMetadata?.());
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return api;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { createCheckpointApi };
|
|
64
|
+
|
|
65
|
+
//# debugId=EDA59BA1E9DE8B8764756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/createCheckpointApi.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import z from 'zod/v4'\n\nimport type { LoadEvents } from './createEventStore.ts'\nimport { ValidationError } from './errors.ts'\nimport type {\n BaseOutputEvent,\n MaybePromise,\n StreamEvents,\n} from './utilityTypes.js'\n\nconst checkpointSchema = z.object({\n lastEventPosition: z.int(),\n type: z.enum(['projection', 'processManager']),\n name: z.string(),\n metadata: z.record(z.string(), z.any()),\n})\n\nexport type Checkpoint = z.output<typeof checkpointSchema>\n\nexport type CheckpointMethods = {\n get: (\n type: 'projection' | 'processManager',\n name: string,\n ) => MaybePromise<Checkpoint | null | undefined>\n delete: (\n type: 'projection' | 'processManager',\n name: string,\n ) => MaybePromise<void>\n upsert: (checkpoint: Checkpoint) => MaybePromise<void>\n}\n\nexport function createCheckpointApi(\n params: CheckpointMethods & {\n loadEvents: LoadEvents\n relatedStreamEvents: StreamEvents[] | null\n name: string\n type: 'processManager' | 'projection'\n apply: (event: BaseOutputEvent) => MaybePromise<void>\n applyAfter?: (event: BaseOutputEvent) => MaybePromise<void>\n },\n) {\n const api = {\n relatedStreamEvents: params.relatedStreamEvents,\n async clear() {\n await params.delete(params.type, params.name)\n },\n async upsert(event: BaseOutputEvent, metadata: Record<string, any> = {}) {\n await params.upsert({\n lastEventPosition: event.position,\n type: params.type,\n name: params.name,\n metadata,\n })\n },\n async rehydrated(\n options: {\n onMetadata?: (metadata: Record<string, any>) => unknown\n getMetadata?: () => Record<string, any>\n onProgress?: (index: number, event: BaseOutputEvent) => void\n } = {},\n ) {\n const checkpoint = await params.get(params.type, params.name)\n let startPosition = 0\n if (checkpoint) {\n const result = checkpointSchema.safeParse(checkpoint)\n if (!result.success) {\n throw new ValidationError(\n `loaded checkpoint for ${params.type} ${params.name} does not comply with schema`,\n result.error,\n )\n }\n startPosition = checkpoint.lastEventPosition + 1\n options.onMetadata?.(checkpoint.metadata)\n }\n\n const events = params.loadEvents(params.relatedStreamEvents, {\n from: startPosition,\n })\n\n let index = 0\n let lastEvent: BaseOutputEvent | null = null\n for await (const event of events) {\n lastEvent = event\n await params.apply(event)\n index++\n options.onProgress?.(index, event)\n }\n\n if (!lastEvent) {\n return // up to date\n }\n\n if (params.applyAfter) {\n for await (const event of events) {\n await params.applyAfter(event)\n }\n }\n\n await api.upsert(lastEvent, options.getMetadata?.())\n },\n }\n\n return api\n}\n\nexport type CheckpointApi = ReturnType<typeof createCheckpointApi>\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;AAAA;AAUA,IAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,mBAAmB,EAAE,IAAI;AAAA,EACzB,MAAM,EAAE,KAAK,CAAC,cAAc,gBAAgB,CAAC;AAAA,EAC7C,MAAM,EAAE,OAAO;AAAA,EACf,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,IAAI,CAAC;AACxC,CAAC;AAgBM,SAAS,mBAAmB,CACjC,QAQA;AAAA,EACA,MAAM,MAAM;AAAA,IACV,qBAAqB,OAAO;AAAA,SACtB,MAAK,GAAG;AAAA,MACZ,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,IAAI;AAAA;AAAA,SAExC,OAAM,CAAC,OAAwB,WAAgC,CAAC,GAAG;AAAA,MACvE,MAAM,OAAO,OAAO;AAAA,QAClB,mBAAmB,MAAM;AAAA,QACzB,MAAM,OAAO;AAAA,QACb,MAAM,OAAO;AAAA,QACb;AAAA,MACF,CAAC;AAAA;AAAA,SAEG,WAAU,CACd,UAII,CAAC,GACL;AAAA,MACA,MAAM,aAAa,MAAM,OAAO,IAAI,OAAO,MAAM,OAAO,IAAI;AAAA,MAC5D,IAAI,gBAAgB;AAAA,MACpB,IAAI,YAAY;AAAA,QACd,MAAM,SAAS,iBAAiB,UAAU,UAAU;AAAA,QACpD,IAAI,CAAC,OAAO,SAAS;AAAA,UACnB,MAAM,IAAI,gBACR,yBAAyB,OAAO,QAAQ,OAAO,oCAC/C,OAAO,KACT;AAAA,QACF;AAAA,QACA,gBAAgB,WAAW,oBAAoB;AAAA,QAC/C,QAAQ,aAAa,WAAW,QAAQ;AAAA,MAC1C;AAAA,MAEA,MAAM,SAAS,OAAO,WAAW,OAAO,qBAAqB;AAAA,QAC3D,MAAM;AAAA,MACR,CAAC;AAAA,MAED,IAAI,QAAQ;AAAA,MACZ,IAAI,YAAoC;AAAA,MACxC,iBAAiB,SAAS,QAAQ;AAAA,QAChC,YAAY;AAAA,QACZ,MAAM,OAAO,MAAM,KAAK;AAAA,QACxB;AAAA,QACA,QAAQ,aAAa,OAAO,KAAK;AAAA,MACnC;AAAA,MAEA,IAAI,CAAC,WAAW;AAAA,QACd;AAAA,MACF;AAAA,MAEA,IAAI,OAAO,YAAY;AAAA,QACrB,iBAAiB,SAAS,QAAQ;AAAA,UAChC,MAAM,OAAO,WAAW,KAAK;AAAA,QAC/B;AAAA,MACF;AAAA,MAEA,MAAM,IAAI,OAAO,WAAW,QAAQ,cAAc,CAAC;AAAA;AAAA,EAEvD;AAAA,EAEA,OAAO;AAAA;",
|
|
8
|
+
"debugId": "EDA59BA1E9DE8B8764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|