@requence/event-sourcing 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-24xjphpk.js +51 -0
  3. package/dist/chunk-24xjphpk.js.map +10 -0
  4. package/dist/chunk-3t1vzbab.js +88 -0
  5. package/dist/chunk-3t1vzbab.js.map +10 -0
  6. package/dist/chunk-6q8mr1sg.js +13 -0
  7. package/dist/chunk-6q8mr1sg.js.map +10 -0
  8. package/dist/chunk-9psg50cb.js +22 -0
  9. package/dist/chunk-9psg50cb.js.map +10 -0
  10. package/dist/chunk-dgzw1nj9.js +50 -0
  11. package/dist/chunk-dgzw1nj9.js.map +10 -0
  12. package/dist/chunk-gq80tgvf.js +70 -0
  13. package/dist/chunk-gq80tgvf.js.map +10 -0
  14. package/dist/chunk-jd4q8fcf.js +11 -0
  15. package/dist/chunk-jd4q8fcf.js.map +10 -0
  16. package/dist/chunk-m07vf8q9.js +210 -0
  17. package/dist/chunk-m07vf8q9.js.map +10 -0
  18. package/dist/chunk-ncpk70p4.js +313 -0
  19. package/dist/chunk-ncpk70p4.js.map +10 -0
  20. package/dist/chunk-pmfzjavb.js +49 -0
  21. package/dist/chunk-pmfzjavb.js.map +10 -0
  22. package/dist/chunk-qdczt68t.js +84 -0
  23. package/dist/chunk-qdczt68t.js.map +10 -0
  24. package/dist/chunk-tfy01fah.js +34 -0
  25. package/dist/chunk-tfy01fah.js.map +10 -0
  26. package/dist/chunk-tkvj6x8k.js +389 -0
  27. package/dist/chunk-tkvj6x8k.js.map +10 -0
  28. package/dist/chunk-ty9bhdvx.js +53 -0
  29. package/dist/chunk-ty9bhdvx.js.map +10 -0
  30. package/dist/chunk-wmz6a1jk.js +233 -0
  31. package/dist/chunk-wmz6a1jk.js.map +10 -0
  32. package/dist/chunk-x90g388y.js +22 -0
  33. package/dist/chunk-x90g388y.js.map +10 -0
  34. package/dist/chunk-z388cd25.js +91 -0
  35. package/dist/chunk-z388cd25.js.map +10 -0
  36. package/dist/chunk-zf3x573k.js +65 -0
  37. package/dist/chunk-zf3x573k.js.map +10 -0
  38. package/dist/createAggregateRoot.d.ts +191 -0
  39. package/dist/createAggregateRoot.d.ts.map +1 -0
  40. package/dist/createAggregateRoot.js +18 -0
  41. package/dist/createAggregateRoot.js.map +9 -0
  42. package/dist/createCheckpointApi.d.ts +38 -0
  43. package/dist/createCheckpointApi.d.ts.map +1 -0
  44. package/dist/createCheckpointApi.js +9 -0
  45. package/dist/createCheckpointApi.js.map +9 -0
  46. package/dist/createEventListener.d.ts +46 -0
  47. package/dist/createEventListener.d.ts.map +1 -0
  48. package/dist/createEventListener.js +12 -0
  49. package/dist/createEventListener.js.map +9 -0
  50. package/dist/createEventStore.d.ts +57 -0
  51. package/dist/createEventStore.d.ts.map +1 -0
  52. package/dist/createEventStore.js +20 -0
  53. package/dist/createEventStore.js.map +9 -0
  54. package/dist/createProcessManager.d.ts +70 -0
  55. package/dist/createProcessManager.d.ts.map +1 -0
  56. package/dist/createProcessManager.js +16 -0
  57. package/dist/createProcessManager.js.map +9 -0
  58. package/dist/createProjection.d.ts +120 -0
  59. package/dist/createProjection.d.ts.map +1 -0
  60. package/dist/createProjection.js +16 -0
  61. package/dist/createProjection.js.map +9 -0
  62. package/dist/drizzle/index.d.ts +449 -0
  63. package/dist/drizzle/index.d.ts.map +1 -0
  64. package/dist/drizzle/index.js +325 -0
  65. package/dist/drizzle/index.js.map +10 -0
  66. package/dist/drizzle/postgres.d.ts +686 -0
  67. package/dist/drizzle/postgres.d.ts.map +1 -0
  68. package/dist/drizzle/postgres.js +18 -0
  69. package/dist/drizzle/postgres.js.map +9 -0
  70. package/dist/errors.d.ts +53 -0
  71. package/dist/errors.d.ts.map +1 -0
  72. package/dist/errors.js +26 -0
  73. package/dist/errors.js.map +9 -0
  74. package/dist/index.d.ts +15 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +73 -0
  77. package/dist/index.js.map +9 -0
  78. package/dist/lock.d.ts +6 -0
  79. package/dist/lock.d.ts.map +1 -0
  80. package/dist/lock.js +8 -0
  81. package/dist/lock.js.map +9 -0
  82. package/dist/memory/index.d.ts +15 -0
  83. package/dist/memory/index.d.ts.map +1 -0
  84. package/dist/memory/index.js +178 -0
  85. package/dist/memory/index.js.map +10 -0
  86. package/dist/redis/index.d.ts +2 -0
  87. package/dist/redis/index.d.ts.map +1 -0
  88. package/dist/redis/index.js +8 -0
  89. package/dist/redis/index.js.map +9 -0
  90. package/dist/redis/redisLock.d.ts +4 -0
  91. package/dist/redis/redisLock.d.ts.map +1 -0
  92. package/dist/redis/redisLock.js +8 -0
  93. package/dist/redis/redisLock.js.map +9 -0
  94. package/dist/refresh.d.ts +4 -0
  95. package/dist/refresh.d.ts.map +1 -0
  96. package/dist/refresh.js +13 -0
  97. package/dist/refresh.js.map +9 -0
  98. package/dist/replay.d.ts +4 -0
  99. package/dist/replay.d.ts.map +1 -0
  100. package/dist/replay.js +13 -0
  101. package/dist/replay.js.map +9 -0
  102. package/dist/snapshotGenerator.d.ts +79 -0
  103. package/dist/snapshotGenerator.d.ts.map +1 -0
  104. package/dist/snapshotGenerator.js +9 -0
  105. package/dist/snapshotGenerator.js.map +9 -0
  106. package/dist/superjson.d.ts +5 -0
  107. package/dist/superjson.d.ts.map +1 -0
  108. package/dist/superjson.js +12 -0
  109. package/dist/superjson.js.map +9 -0
  110. package/dist/transaction.d.ts +13 -0
  111. package/dist/transaction.d.ts.map +1 -0
  112. package/dist/transaction.js +8 -0
  113. package/dist/transaction.js.map +9 -0
  114. package/dist/utilityTypes.d.js +2 -0
  115. package/dist/utilityTypes.d.js.map +9 -0
  116. package/dist/utils.d.ts +4 -0
  117. package/dist/utils.d.ts.map +1 -0
  118. package/dist/utils.js +10 -0
  119. package/dist/utils.js.map +9 -0
  120. package/dist/wrappedEventBuilder.d.ts +37 -0
  121. package/dist/wrappedEventBuilder.d.ts.map +1 -0
  122. package/dist/wrappedEventBuilder.js +12 -0
  123. package/dist/wrappedEventBuilder.js.map +9 -0
  124. package/package.json +85 -1
  125. package/src/createAggregateRoot.ts +902 -0
  126. package/src/createCheckpointApi.ts +106 -0
  127. package/src/createEventListener.ts +118 -0
  128. package/src/createEventStore.ts +446 -0
  129. package/src/createProcessManager.ts +363 -0
  130. package/src/createProjection.ts +577 -0
  131. package/src/drizzle/index.ts +449 -0
  132. package/src/drizzle/postgres.ts +120 -0
  133. package/src/errors.ts +85 -0
  134. package/src/index.ts +45 -0
  135. package/src/lock.ts +58 -0
  136. package/src/memory/index.ts +224 -0
  137. package/src/redis/index.ts +1 -0
  138. package/src/redis/redisLock.ts +61 -0
  139. package/src/refresh.ts +19 -0
  140. package/src/replay.ts +19 -0
  141. package/src/snapshotGenerator.ts +190 -0
  142. package/src/superjson.ts +11 -0
  143. package/src/tests/aggregateRoot.spec.ts +532 -0
  144. package/src/tests/eventListener.spec.ts +57 -0
  145. package/src/tests/eventStore.spec.ts +86 -0
  146. package/src/tests/processManager.spec.ts +185 -0
  147. package/src/tests/projection.spec.ts +511 -0
  148. package/src/tests/projectionProcessManager.spec.ts +203 -0
  149. package/src/tests/setup.ts +230 -0
  150. package/src/tests/transaction.spec.ts +185 -0
  151. package/src/transaction.ts +93 -0
  152. package/src/utilityTypes.d.ts +110 -0
  153. package/src/utils.ts +43 -0
  154. package/src/wrappedEventBuilder.ts +181 -0
@@ -0,0 +1,389 @@
1
+ import {
2
+ handleEventError,
3
+ unwrapEvent,
4
+ wrappedEventBuilder
5
+ } from "./chunk-z388cd25.js";
6
+ import {
7
+ getProjectionInfo
8
+ } from "./chunk-ncpk70p4.js";
9
+ import {
10
+ getProcessManagerInfo
11
+ } from "./chunk-m07vf8q9.js";
12
+ import {
13
+ clone
14
+ } from "./chunk-6q8mr1sg.js";
15
+ import {
16
+ isRefreshing
17
+ } from "./chunk-9psg50cb.js";
18
+ import {
19
+ awaitAll,
20
+ normalizeArray
21
+ } from "./chunk-tfy01fah.js";
22
+ import {
23
+ createSnapshotGenerator
24
+ } from "./chunk-pmfzjavb.js";
25
+ import {
26
+ CommandError,
27
+ EventValidationError,
28
+ InfiniteLoopError,
29
+ ValidationError
30
+ } from "./chunk-qdczt68t.js";
31
+
32
+ // src/createAggregateRoot.ts
33
+ import { AsyncLocalStorage } from "node:async_hooks";
34
+ import { deepEqual } from "fast-equals";
35
+ import { z } from "zod/v4";
36
+ var downstreamRootsPromiseStore = new AsyncLocalStorage;
37
+ function registerRootPromiseToUpstream(rootPromise) {
38
+ downstreamRootsPromiseStore.getStore()?.push(rootPromise);
39
+ }
40
+ async function collectDownstreamRootPromise(handler) {
41
+ const rootPromises = [];
42
+ await downstreamRootsPromiseStore.run(rootPromises, handler);
43
+ return async () => {
44
+ await Promise.all(rootPromises.map((promise) => promise()));
45
+ };
46
+ }
47
+ var snapshotSchema = z.object({
48
+ streamId: z.string(),
49
+ schemaVersion: z.int(),
50
+ state: z.unknown(),
51
+ createdAt: z.date(),
52
+ streamVersion: z.int(),
53
+ streamType: z.string(),
54
+ lastEventPosition: z.int(),
55
+ metadata: z.record(z.string(), z.unknown()).optional()
56
+ });
57
+ function createAggregateRoot(type) {
58
+ let initialState = null;
59
+ let createEventHandlers = () => ({});
60
+ let loadEvents;
61
+ let appendEvents;
62
+ let emitEvents;
63
+ let snapshots;
64
+ let createLock;
65
+ let transaction;
66
+ let createCommands = () => ({});
67
+ const registeredEvents = new Map;
68
+ let createSnapshotApi = null;
69
+ const loadUntil = async (state, snapshotApi, id, untilPosition, filter) => {
70
+ let position = 0;
71
+ let version = 0;
72
+ if (snapshotApi) {
73
+ const snapshot = await snapshots?.get({
74
+ stream: type,
75
+ id
76
+ });
77
+ if (snapshot) {
78
+ const result = snapshotSchema.safeParse(snapshot);
79
+ if (!result.success) {
80
+ throw new ValidationError("loaded aggregate root snapshot does not comply with schema", result.error);
81
+ }
82
+ snapshotApi.internalApply(result.data);
83
+ position = snapshot.lastEventPosition;
84
+ version = snapshot.streamVersion;
85
+ }
86
+ }
87
+ const events = loadEvents({ stream: type, id }, {
88
+ from: position,
89
+ to: untilPosition
90
+ });
91
+ const eventHandlers = createEventHandlers(state);
92
+ for await (const event of events) {
93
+ version += 1;
94
+ if (await filter?.(event) === false) {
95
+ continue;
96
+ }
97
+ await eventHandlers[`on${event.type}`]?.(event);
98
+ }
99
+ return version;
100
+ };
101
+ const createStream = (state, streamId) => {
102
+ let version = 0;
103
+ const snapshotApi = createSnapshotApi?.(state, createSnapshotGenerator(), { z });
104
+ const errorHandlers = new Map;
105
+ let downstreamSettledPromise = () => Promise.resolve();
106
+ let releaseLock;
107
+ let extendLock;
108
+ const warnReleaseLock = () => {
109
+ if (releasedAfterChain) {
110
+ console.warn(`aggregate root for ${type}:${streamId} was open too long`);
111
+ }
112
+ releaseLock();
113
+ };
114
+ const processEvents = async (events) => {
115
+ if (!appendEvents) {
116
+ throw new Error(`aggregate root ${type} is not linked to an event store`);
117
+ }
118
+ const persistedEvents = await appendEvents({ stream: type, id: streamId }, events, version);
119
+ warnReleaseLock();
120
+ for (const persistedEvent of persistedEvents) {
121
+ const snapshot = await snapshotApi?.internalCapture(persistedEvent, persistedEvent.streamVersion);
122
+ if (snapshot) {
123
+ await snapshots?.put({
124
+ state: snapshot.state,
125
+ lastEventPosition: persistedEvent.position,
126
+ streamId,
127
+ streamVersion: version,
128
+ streamType: type,
129
+ schemaVersion: snapshot.version,
130
+ createdAt: new Date
131
+ });
132
+ }
133
+ }
134
+ version += events.length;
135
+ downstreamSettledPromise = await collectDownstreamRootPromise(() => emitEvents(persistedEvents));
136
+ };
137
+ let commandChain = createLock([type, streamId]).then((lock) => {
138
+ releaseLock = lock.release;
139
+ extendLock = lock.extend;
140
+ return [];
141
+ });
142
+ let commandChainTouched = false;
143
+ let releasedAfterChain = false;
144
+ let transactionDelay;
145
+ const stateReady = Promise.withResolvers();
146
+ const rawCommands = createCommands(state, wrappedEventBuilder, {
147
+ streamId,
148
+ extendLock
149
+ });
150
+ const commands = Object.fromEntries(Object.entries(rawCommands).map(([commandName, commandFn]) => [
151
+ commandName,
152
+ (...args) => {
153
+ if (isRefreshing()) {
154
+ return api2.commands;
155
+ }
156
+ const commandId = crypto.randomUUID();
157
+ transactionDelay ??= transaction.delay();
158
+ commandChainTouched = true;
159
+ commandChain = commandChain.then(async (previousEvents) => {
160
+ if (previousEvents instanceof Error) {
161
+ return previousEvents;
162
+ }
163
+ const events = (await normalizeArray(commandFn.apply(rawCommands, args), () => extendLock())).filter((event) => Boolean(event));
164
+ const baseInputEvents = await Promise.all(events.map(async (event) => {
165
+ event.withAdditionalMetadata({ commandName });
166
+ event.withDefaultCausationId(commandId);
167
+ const unwrappedEvent = unwrapEvent(event);
168
+ const eventVersions = registeredEvents.get(unwrappedEvent.type);
169
+ if (!eventVersions) {
170
+ throw new Error(`cannot append unknown event ${unwrappedEvent.type}`);
171
+ }
172
+ const latestVersion = Math.max(...Array.from(eventVersions.keys()));
173
+ const schema = eventVersions.get(latestVersion);
174
+ const result = schema.safeParse(unwrappedEvent.payload);
175
+ if (!result.success) {
176
+ const error = new EventValidationError(`payload does not comply with schema for event ${unwrappedEvent.type}@${latestVersion} at stream ${streamId}`, result.error);
177
+ await handleEventError(event, error);
178
+ throw error;
179
+ }
180
+ unwrappedEvent.payload = result.data;
181
+ return {
182
+ ...unwrappedEvent,
183
+ schemaVersion: latestVersion
184
+ };
185
+ }));
186
+ const eventHandlers = createEventHandlers(state);
187
+ for (const event of baseInputEvents) {
188
+ const processManagerInfo = getProcessManagerInfo();
189
+ if (processManagerInfo) {
190
+ if (processManagerInfo.event.streamId === streamId && processManagerInfo.event.streamType === type && processManagerInfo.event.type === event.type && deepEqual(processManagerInfo.event.payload, event.payload)) {
191
+ return new InfiniteLoopError(`Infinite loop detected. Process Manager "${processManagerInfo.name}" listens and emits same event "${event.type}" for stream ${type}:${streamId}, causing a recurring event cycle`);
192
+ }
193
+ }
194
+ await eventHandlers[`on${event.type}`]?.({
195
+ ...event,
196
+ streamId,
197
+ streamType: type
198
+ });
199
+ }
200
+ return [...previousEvents, ...baseInputEvents];
201
+ }).catch((error) => {
202
+ if (!(error instanceof Error)) {
203
+ throw error;
204
+ }
205
+ const commandError = new CommandError(type, commandName, streamId, error);
206
+ for (const [ErrorType, handler] of errorHandlers) {
207
+ if (error instanceof ErrorType) {
208
+ return new CommandError(type, commandName, streamId, handler(commandError));
209
+ }
210
+ }
211
+ return commandError;
212
+ });
213
+ const currentCommandChain = commandChain;
214
+ currentCommandChain.then(async (allEvents) => {
215
+ if (currentCommandChain === commandChain) {
216
+ stateReady.resolve(allEvents instanceof Error ? allEvents : undefined);
217
+ commandChain = commandChain.then(async () => {
218
+ if (Array.isArray(allEvents)) {
219
+ transactionDelay?.resolve();
220
+ try {
221
+ await transactionDelay?.commit();
222
+ } catch (error) {
223
+ if (error instanceof Error) {
224
+ warnReleaseLock();
225
+ return error;
226
+ }
227
+ }
228
+ await processEvents(allEvents);
229
+ return [];
230
+ }
231
+ warnReleaseLock();
232
+ transactionDelay?.reject(allEvents);
233
+ if (transaction.active) {
234
+ return [];
235
+ }
236
+ throw allEvents;
237
+ });
238
+ }
239
+ });
240
+ return api2.commands;
241
+ }
242
+ ]));
243
+ setImmediate(() => {
244
+ if (!commandChainTouched) {
245
+ releasedAfterChain = true;
246
+ commandChain = commandChain.then((v) => {
247
+ releaseLock();
248
+ return v;
249
+ });
250
+ }
251
+ });
252
+ const api2 = {
253
+ load(filter) {
254
+ commandChain = commandChain.then(() => loadUntil(state, snapshotApi, streamId, undefined, filter)).then((loadedVersion) => {
255
+ version = loadedVersion;
256
+ return [];
257
+ });
258
+ },
259
+ commands: {
260
+ ...commands,
261
+ async state() {
262
+ const maybeError = await Promise.race([
263
+ stateReady.promise,
264
+ awaitAll(() => commandChain)
265
+ ]);
266
+ if (maybeError) {
267
+ throw maybeError;
268
+ }
269
+ return state;
270
+ },
271
+ async settled() {
272
+ const parentProcessManager = getProcessManagerInfo();
273
+ if (parentProcessManager) {
274
+ const error = new Error(`aggregateRoot.settled() cannot be called inside a process manager. Attempted on aggregate stream "${type}" within Process Manager ${parentProcessManager.name}.`);
275
+ commandChain = commandChain.then(() => error);
276
+ throw error;
277
+ }
278
+ if (transaction.active) {
279
+ const error = new Error(`aggregateRoot.settled() cannot be called inside a transaction. Attempted on aggregate stream "${type}".`);
280
+ commandChain = commandChain.then(() => error);
281
+ throw error;
282
+ }
283
+ await awaitAll(() => commandChain);
284
+ await downstreamSettledPromise();
285
+ return api2.commands;
286
+ },
287
+ transformError(error, handler) {
288
+ errorHandlers.set(error, handler);
289
+ return api2.commands;
290
+ },
291
+ streamId
292
+ }
293
+ };
294
+ registerRootPromiseToUpstream(() => api2.commands.settled());
295
+ transaction.after(() => api2.commands.settled());
296
+ return api2;
297
+ };
298
+ const api = {
299
+ type,
300
+ withInitialState: (state) => {
301
+ initialState = clone(state);
302
+ return api;
303
+ },
304
+ withCommands(handlers) {
305
+ createCommands = (state, eventBuilder, options) => {
306
+ if (state === null) {
307
+ return handlers(eventBuilder, options);
308
+ }
309
+ return handlers(state, eventBuilder, options);
310
+ };
311
+ return api;
312
+ },
313
+ withEvents(handler) {
314
+ const eventMap = typeof handler === "function" ? handler({ version: (version, schema) => ({ version, schema }), z }) : handler;
315
+ for (const [type2, config] of Object.entries(eventMap)) {
316
+ const versions = Array.isArray(config) ? config : [config];
317
+ for (const v of versions) {
318
+ let schema;
319
+ let version;
320
+ if (typeof v === "object" && "version" in v) {
321
+ schema = v.schema;
322
+ version = v.version;
323
+ } else {
324
+ schema = v;
325
+ version = 0;
326
+ }
327
+ if (registeredEvents.get(type2)?.has(version)) {
328
+ throw new Error(`event ${type2} is already registered in version ${version}`);
329
+ }
330
+ if (!registeredEvents.has(type2)) {
331
+ registeredEvents.set(type2, new Map);
332
+ }
333
+ registeredEvents.get(type2).set(version, schema);
334
+ }
335
+ }
336
+ return api;
337
+ },
338
+ withEventHandlers(handler) {
339
+ createEventHandlers = handler;
340
+ return api;
341
+ },
342
+ withSnapshots: (handler) => {
343
+ createSnapshotApi = handler;
344
+ return api;
345
+ },
346
+ newStream(id) {
347
+ if (!loadEvents) {
348
+ throw new Error(`aggregate root ${type} is not linked to an event store`);
349
+ }
350
+ const parentProjectionName = getProjectionInfo()?.name;
351
+ if (parentProjectionName) {
352
+ throw new Error(`aggregateRoot.newStream() cannot be called inside a projection. Attempted on aggregate stream ${type} within Projection ${parentProjectionName}.`);
353
+ }
354
+ return createStream(clone(initialState), id ?? crypto.randomUUID()).commands;
355
+ },
356
+ loadStream(id, filter) {
357
+ if (!loadEvents) {
358
+ throw new Error(`aggregate root ${type} is not linked to an event store`);
359
+ }
360
+ const parentProjectionName = getProjectionInfo()?.name;
361
+ if (parentProjectionName) {
362
+ throw new Error(`aggregateRoot.loadStream("${id}") cannot be called inside a projection. Attempted on aggregate stream ${type} within Projection ${parentProjectionName}.`);
363
+ }
364
+ const root = createStream(clone(initialState), id);
365
+ root.load(filter);
366
+ return root.commands;
367
+ },
368
+ linkEventStore(load, append, emit, trans, lock, snap) {
369
+ loadEvents = load;
370
+ appendEvents = append;
371
+ emitEvents = emit;
372
+ transaction = trans;
373
+ createLock = lock;
374
+ snapshots = snap;
375
+ },
376
+ get eventTypes() {
377
+ return Array.from(registeredEvents.keys());
378
+ },
379
+ $inferEvents: null,
380
+ $inferEvent: null,
381
+ $inferState: null,
382
+ $inferCommandParameters: null
383
+ };
384
+ return api;
385
+ }
386
+
387
+ export { createAggregateRoot };
388
+
389
+ //# debugId=7975EF0D4D2A5E9964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/createAggregateRoot.ts"],
4
+ "sourcesContent": [
5
+ "import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport { deepEqual } from 'fast-equals'\nimport { z } from 'zod/v4'\n\nimport type {\n EmitEvents,\n InternalAppendEvents,\n LoadEvents,\n} from './createEventStore.ts'\nimport { getProcessManagerInfo } from './createProcessManager.ts'\nimport { getProjectionInfo } from './createProjection.ts'\nimport {\n CommandError,\n EventValidationError,\n InfiniteLoopError,\n ValidationError,\n} from './errors.ts'\nimport type { LockCreator } from './lock.ts'\nimport { isRefreshing } from './refresh.ts'\nimport createSnapshotGenerator, {\n type CompleteSnapshotGenerator,\n type InternalSnapshotGenerator,\n type SnapshotGenerator,\n} from './snapshotGenerator.ts'\nimport { clone } from './superjson.ts'\nimport type { Transaction, TransactionDelay } from './transaction.ts'\nimport type {\n BaseInputEvent,\n BaseOutputEvent,\n EventTemplate,\n Keys,\n MaybePromise,\n Merge,\n OutputEvents,\n Prettify,\n} from './utilityTypes.ts'\nimport { awaitAll, normalizeArray } from './utils.ts'\nimport {\n type WrappedEvent,\n type WrappedEventBuilder,\n handleEventError,\n unwrapEvent,\n wrappedEventBuilder,\n} from './wrappedEventBuilder.ts'\n\ntype UppercaseRecord<T extends Record<string, any>> = {\n [K in keyof T]: K extends Capitalize<K & string>\n ? any\n : `Error: Key '${K & string}' must be Capitalized`\n}\n\ntype BaseEventMap = Record<\n string,\n z.ZodType | (z.ZodType | { schema: z.ZodType; version: number })[]\n>\n\ntype DownstreamRootPromise = () => Promise<unknown>\nconst downstreamRootsPromiseStore = new AsyncLocalStorage<\n DownstreamRootPromise[]\n>()\n\nfunction registerRootPromiseToUpstream(rootPromise: DownstreamRootPromise) {\n downstreamRootsPromiseStore.getStore()?.push(rootPromise)\n}\n\nasync function collectDownstreamRootPromise(handler: () => Promise<void>) {\n const rootPromises: DownstreamRootPromise[] = []\n await downstreamRootsPromiseStore.run(rootPromises, handler)\n return async () => {\n await Promise.all(rootPromises.map((promise) => promise()))\n }\n}\n\ntype BaseInternalEvent<\n Type extends string,\n Version extends number,\n Payload = unknown,\n> = BaseOutputEvent<Type, Version, Payload>\n\ntype InternalEvent<Event extends EventTemplate> = Prettify<\n {\n [K in Event as K['version']]: Omit<\n BaseInternalEvent<K['type'], K['version'], z.output<K['schema']>>,\n 'createdAt' | 'position' | 'streamVersion'\n >\n }[Event['version']]\n>\n\ntype InternalEventHandlers<Events extends EventTemplate> = {\n [K in Events as `on${K['type']}`]?: (\n event: InternalEvent<K>,\n ) => MaybePromise<void>\n}\n\ntype EventsFromMap<EventMap extends BaseEventMap> = Prettify<\n {\n [K in keyof EventMap]: { type: K & string } & (EventMap[K] extends z.ZodType\n ? { schema: EventMap[K]; version: 0 }\n : EventMap[K] extends { schema: infer S; version: infer V }\n ? S extends z.ZodType\n ? { schema: S; version: V }\n : never\n : EventMap[K] extends Array<infer I>\n ? I extends z.ZodType\n ? { schema: I; version: 0 }\n : I extends { schema: infer S; version: infer V }\n ? { schema: S; version: V }\n : never\n : never)\n }[keyof EventMap]\n>\n\ntype Constructor<T> = new (...args: any[]) => T\n\ntype Command = (\n ...args: any[]\n) =>\n | MaybePromise<void | WrappedEvent | WrappedEvent[]>\n | Iterable<WrappedEvent>\n | AsyncIterable<WrappedEvent>\n\ntype Utils = {\n reset: () => void\n replayUntil: (streamVersion: number) => Promise<void>\n}\n\ntype VersionBuilder = <V extends number, S extends z.ZodType>(\n version: V,\n schema: S,\n) => { schema: S; version: V }\n\ntype Stream<Commands extends Record<string, Command>, State> = {\n [A in keyof Commands]: (\n ...args: Parameters<Commands[A]>\n ) => Stream<Commands, State>\n} & {\n settled: () => Promise<Stream<Commands, State>>\n state: () => Promise<State>\n transformError: <T extends Error>(\n error: Constructor<T>,\n errorHandler: (commandError: CommandError<T>) => Error,\n ) => Stream<Commands, State>\n readonly streamId: string\n}\n\nexport type AnyStream = {\n settled: () => Promise<AnyStream>\n state: () => Promise<unknown>\n transformError: <T extends Error>(\n error: Constructor<T>,\n errorHandler: (error: CommandError<T>) => Error,\n ) => AnyStream\n}\n\nexport type AnyAggregateRoot = {\n type: string\n loadStream: (\n id: string,\n filter?: (event: BaseOutputEvent) => MaybePromise<boolean>,\n ) => AnyStream\n newStream: () => AnyStream\n $inferState: any\n $inferEvents: any\n $inferEvent: any\n $inferCommandParameters: any\n}\n\ntype EventHandlerOptions = {\n streamId: string\n extendLock: (ms?: number) => Promise<boolean>\n}\n\nexport type AggregateRootApi<\n Name,\n State = never,\n Events extends EventTemplate = never,\n Commands extends Record<string, Command> = Record<string, Command>,\n Flags extends {\n withInitialState: boolean\n withEvents: boolean\n withCommands: boolean\n withEventHandlers: boolean\n withSnapshots: boolean\n } = {\n withInitialState: false\n withEvents: false\n withCommands: true\n withEventHandlers: true\n withSnapshots: false\n },\n> = {\n withInitialState: <NextState extends Record<string, unknown>>(\n state: NextState,\n ) => Omit<\n AggregateRootApi<\n Name,\n NextState,\n Events,\n Commands,\n Merge<Flags, { withInitialState: true; withEventHandlers: false }>\n >,\n Keys<Merge<Flags, { withInitialState: true; withEventHandlers: false }>>\n >\n withCommands: <\n NextCommands extends Record<\n Exclude<string, 'state' | 'settled' | 'transformError' | 'streamId'>,\n Command\n >,\n >(\n handlers: [State] extends [never]\n ? (\n event: WrappedEventBuilder<Events>,\n options: EventHandlerOptions,\n ) => NextCommands\n : (\n state: Readonly<State>,\n event: WrappedEventBuilder<Events>,\n options: EventHandlerOptions,\n ) => NextCommands,\n ) => Omit<\n AggregateRootApi<\n Name,\n State,\n Events,\n NextCommands,\n Merge<\n Flags,\n { withInitialState: true; withCommands: true; withEvents: true }\n >\n >,\n Keys<\n Merge<\n Flags,\n { withInitialState: true; withCommands: true; withEvents: true }\n >\n >\n >\n withEvents: {\n <EventMap extends BaseEventMap>(\n handler: EventMap & UppercaseRecord<EventMap>,\n ): Omit<\n AggregateRootApi<\n Name,\n State,\n EventsFromMap<EventMap>,\n Commands,\n Merge<\n Flags,\n {\n withInitialState: true\n withEvents: true\n withCommands: false\n }\n >\n >,\n Keys<\n Merge<\n Flags,\n {\n withInitialState: true\n withEvents: true\n withCommands: false\n }\n >\n >\n >\n <EventMap extends BaseEventMap>(\n handler: (utils: {\n version: VersionBuilder\n z: typeof z\n }) => EventMap & UppercaseRecord<EventMap>,\n ): Omit<\n AggregateRootApi<\n Name,\n State,\n EventsFromMap<EventMap>,\n Commands,\n Merge<\n Flags,\n {\n withInitialState: true\n withEvents: true\n withCommands: false\n }\n >\n >,\n Keys<\n Merge<\n Flags,\n {\n withInitialState: true\n withEvents: true\n withCommands: false\n }\n >\n >\n >\n }\n withEventHandlers: (\n handler: (state: State, utils: Utils) => InternalEventHandlers<Events>,\n ) => Omit<\n AggregateRootApi<\n Name,\n State,\n Events,\n Commands,\n Merge<\n Flags,\n { withInitialState: true; withEventHandlers: true; withEvents: true }\n >\n >,\n Keys<\n Merge<\n Flags,\n { withInitialState: true; withEventHandlers: true; withEvents: true }\n >\n >\n >\n withSnapshots: (\n handler: (\n state: State,\n generate: SnapshotGenerator<Omit<Snapshot, 'schemaVersion' | 'state'>>,\n utils: { z: typeof z },\n ) => CompleteSnapshotGenerator,\n ) => Omit<\n AggregateRootApi<\n Name,\n State,\n Events,\n Commands,\n Merge<Flags, { withSnapshots: true }>\n >,\n Keys<Merge<Flags, { withSnapshots: true }>>\n >\n loadStream: (\n id: string,\n filter?: (event: OutputEvents<Events>) => MaybePromise<boolean>,\n ) => Stream<Commands, State>\n newStream: (id?: string) => Stream<Commands, State>\n\n $inferEvents: OutputEvents<Events>\n $inferEvent: OutputEvents<Events> extends BaseOutputEvent\n ? { [E in OutputEvents<Events> as E['type']]: E }\n : never\n $inferState: State\n $inferCommandParameters: {\n [CommandName in keyof Commands]: Parameters<Commands[CommandName]>\n }\n readonly type: Name\n}\n\nexport type InternalAggregateRootApi = {\n linkEventStore: (\n loadEvents: LoadEvents,\n appendEvents: InternalAppendEvents,\n emitEvents: EmitEvents,\n transaction: Transaction,\n lock: LockCreator,\n snapshots?: AggregateRootSnapshotMethods,\n ) => void\n eventTypes: string[]\n readonly type: string\n}\n\nconst snapshotSchema = z.object({\n streamId: z.string(),\n schemaVersion: z.int(),\n state: z.unknown(),\n createdAt: z.date(),\n streamVersion: z.int(),\n streamType: z.string(),\n lastEventPosition: z.int(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n})\n\nexport type Snapshot = z.output<typeof snapshotSchema>\n\nexport type AggregateRootSnapshotMethods = {\n get: (select: {\n stream: string\n id: string\n }) => MaybePromise<Snapshot | null | undefined>\n put: (snapshot: Snapshot) => MaybePromise<void>\n delete: (\n target: {\n stream: string\n id: string\n },\n fromPosition: number,\n ) => MaybePromise<void>\n}\n\nexport function createAggregateRoot<Name extends string>(type: Name) {\n let initialState: any = null\n let createEventHandlers: (\n state: any,\n ) => InternalEventHandlers<any> = () => ({})\n\n let loadEvents!: LoadEvents\n let appendEvents!: InternalAppendEvents\n let emitEvents!: EmitEvents\n let snapshots: AggregateRootSnapshotMethods | undefined\n let createLock!: LockCreator\n let transaction!: Transaction\n\n let createCommands: (\n state: any,\n eventBuilder: (name: string, payload: unknown) => WrappedEvent,\n options: EventHandlerOptions,\n ) => Record<string, Command> = () => ({})\n\n const registeredEvents = new Map<string, Map<number, z.ZodType>>()\n let createSnapshotApi:\n | ((\n state: any,\n generate: SnapshotGenerator<any>,\n utils: { z: typeof z },\n ) => CompleteSnapshotGenerator & InternalSnapshotGenerator)\n | null = null\n\n const loadUntil = async (\n state: any,\n snapshotApi:\n | (CompleteSnapshotGenerator & InternalSnapshotGenerator)\n | undefined,\n id: string,\n untilPosition?: number,\n filter?: (event: BaseOutputEvent) => MaybePromise<boolean>,\n ) => {\n let position = 0\n let version = 0\n if (snapshotApi) {\n const snapshot = await snapshots?.get({\n stream: type,\n id,\n })\n if (snapshot) {\n const result = snapshotSchema.safeParse(snapshot)\n\n if (!result.success) {\n throw new ValidationError(\n 'loaded aggregate root snapshot does not comply with schema',\n result.error,\n )\n }\n\n snapshotApi.internalApply(result.data)\n position = snapshot.lastEventPosition\n version = snapshot.streamVersion\n }\n }\n\n const events = loadEvents(\n { stream: type, id },\n {\n from: position,\n to: untilPosition,\n },\n )\n const eventHandlers = createEventHandlers(state)\n for await (const event of events) {\n version += 1\n if ((await filter?.(event)) === false) {\n continue\n }\n await (eventHandlers as any)[`on${event.type}`]?.(event)\n }\n\n return version\n }\n\n const createStream = (state: any, streamId: string) => {\n let version = 0\n const snapshotApi = createSnapshotApi?.(\n state,\n createSnapshotGenerator() as any,\n { z },\n )\n const errorHandlers = new Map<\n Constructor<Error>,\n (error: CommandError<Error>) => Error\n >()\n let downstreamSettledPromise = () => Promise.resolve()\n let releaseLock!: () => Promise<void>\n let extendLock!: EventHandlerOptions['extendLock']\n\n const warnReleaseLock = () => {\n if (releasedAfterChain) {\n console.warn(`aggregate root for ${type}:${streamId} was open too long`)\n }\n releaseLock()\n }\n\n const processEvents = async (events: BaseInputEvent[]) => {\n if (!appendEvents) {\n throw new Error(\n `aggregate root ${type} is not linked to an event store`,\n )\n }\n\n const persistedEvents = await appendEvents(\n { stream: type, id: streamId },\n events,\n version,\n )\n\n warnReleaseLock()\n\n for (const persistedEvent of persistedEvents) {\n const snapshot = await snapshotApi?.internalCapture(\n persistedEvent,\n persistedEvent.streamVersion,\n )\n\n if (snapshot) {\n await snapshots?.put({\n state: snapshot.state,\n lastEventPosition: persistedEvent.position,\n streamId,\n streamVersion: version,\n streamType: type,\n schemaVersion: snapshot.version,\n createdAt: new Date(),\n })\n }\n }\n\n version += events.length\n\n downstreamSettledPromise = await collectDownstreamRootPromise(() =>\n emitEvents(persistedEvents),\n )\n }\n\n let commandChain = createLock([type, streamId]).then((lock) => {\n releaseLock = lock.release\n extendLock = lock.extend\n return [] as Error | BaseInputEvent[]\n })\n let commandChainTouched = false\n let releasedAfterChain = false\n let transactionDelay: TransactionDelay | undefined\n\n const stateReady = Promise.withResolvers<Error | undefined>()\n\n const rawCommands = createCommands(state, wrappedEventBuilder, {\n streamId,\n extendLock,\n })\n\n const commands = Object.fromEntries(\n Object.entries(rawCommands).map(([commandName, commandFn]) => [\n commandName,\n (...args: any[]) => {\n // don't create new events when refreshing the process manager\n if (isRefreshing()) {\n return api.commands\n }\n\n const commandId = crypto.randomUUID()\n transactionDelay ??= transaction.delay()\n commandChainTouched = true\n commandChain = commandChain\n .then(async (previousEvents) => {\n if (previousEvents instanceof Error) {\n return previousEvents\n }\n const events = (\n await normalizeArray(\n commandFn.apply(rawCommands, args),\n () => extendLock(),\n )\n ).filter((event) => Boolean(event))\n\n const baseInputEvents = await Promise.all(\n events.map(async (event) => {\n event.withAdditionalMetadata({ commandName })\n event.withDefaultCausationId(commandId)\n\n const unwrappedEvent = unwrapEvent(event)\n\n const eventVersions = registeredEvents.get(\n unwrappedEvent.type,\n )\n if (!eventVersions) {\n throw new Error(\n `cannot append unknown event ${unwrappedEvent.type}`,\n )\n }\n const latestVersion = Math.max(\n ...Array.from(eventVersions.keys()),\n )\n const schema = eventVersions.get(latestVersion)!\n\n const result = schema.safeParse(unwrappedEvent.payload)\n if (!result.success) {\n const error = new EventValidationError(\n `payload does not comply with schema for event ${unwrappedEvent.type}@${latestVersion} at stream ${streamId}`,\n result.error,\n )\n\n await handleEventError(event, error)\n throw error\n }\n\n unwrappedEvent.payload = result.data\n\n return {\n ...unwrappedEvent,\n schemaVersion: latestVersion,\n }\n }),\n )\n\n const eventHandlers = createEventHandlers(state)\n for (const event of baseInputEvents) {\n const processManagerInfo = getProcessManagerInfo()\n if (processManagerInfo) {\n if (\n processManagerInfo.event.streamId === streamId &&\n processManagerInfo.event.streamType === type &&\n processManagerInfo.event.type === event.type &&\n deepEqual(processManagerInfo.event.payload, event.payload)\n ) {\n return new InfiniteLoopError(\n `Infinite loop detected. Process Manager \"${processManagerInfo.name}\" listens and emits same event \"${event.type}\" for stream ${type}:${streamId}, causing a recurring event cycle`,\n )\n }\n }\n\n await eventHandlers[`on${event.type}`]?.({\n ...event,\n streamId,\n streamType: type,\n })\n }\n\n return [...previousEvents, ...baseInputEvents]\n })\n .catch((error) => {\n if (!(error instanceof Error)) {\n throw error\n }\n\n const commandError = new CommandError(\n type,\n commandName,\n streamId,\n error,\n )\n for (const [ErrorType, handler] of errorHandlers) {\n if (error instanceof ErrorType) {\n return new CommandError(\n type,\n commandName,\n streamId,\n handler(commandError),\n )\n }\n }\n\n return commandError\n })\n\n const currentCommandChain = commandChain\n currentCommandChain.then(async (allEvents) => {\n if (\n // we are the last command\n currentCommandChain === commandChain\n ) {\n stateReady.resolve(\n allEvents instanceof Error ? allEvents : undefined,\n )\n\n commandChain = commandChain.then(async () => {\n // no Error in chain\n if (Array.isArray(allEvents)) {\n transactionDelay?.resolve()\n try {\n await transactionDelay?.commit()\n } catch (error) {\n if (error instanceof Error) {\n warnReleaseLock()\n return error\n }\n }\n\n await processEvents(allEvents)\n return []\n }\n\n warnReleaseLock()\n transactionDelay?.reject(allEvents)\n if (transaction.active) {\n return []\n }\n\n throw allEvents\n })\n }\n })\n\n return api.commands\n },\n ]),\n )\n\n setImmediate(() => {\n if (!commandChainTouched) {\n releasedAfterChain = true\n commandChain = commandChain.then((v) => {\n releaseLock()\n return v\n })\n }\n })\n\n const api = {\n load(filter?: (event: BaseOutputEvent) => MaybePromise<boolean>) {\n // ensure commands are executed after load is finished\n commandChain = commandChain\n .then(() =>\n loadUntil(state, snapshotApi, streamId, undefined, filter),\n )\n .then((loadedVersion) => {\n version = loadedVersion\n return []\n })\n },\n commands: {\n ...commands,\n async state() {\n const maybeError = await Promise.race([\n stateReady.promise,\n awaitAll(() => commandChain),\n ])\n\n if (maybeError) {\n throw maybeError\n }\n\n return state\n },\n async settled() {\n const parentProcessManager = getProcessManagerInfo()\n if (parentProcessManager) {\n const error = new Error(\n `aggregateRoot.settled() cannot be called inside a process manager. Attempted on aggregate stream \"${type}\" within Process Manager ${parentProcessManager.name}.`,\n )\n commandChain = commandChain.then(() => error)\n throw error\n }\n\n if (transaction.active) {\n const error = new Error(\n `aggregateRoot.settled() cannot be called inside a transaction. Attempted on aggregate stream \"${type}\".`,\n )\n commandChain = commandChain.then(() => error)\n throw error\n }\n\n await awaitAll(() => commandChain)\n await downstreamSettledPromise()\n return api.commands\n },\n transformError(\n error: Constructor<Error>,\n handler: (error: Error) => never,\n ) {\n errorHandlers.set(error, handler)\n return api.commands\n },\n streamId,\n },\n }\n\n registerRootPromiseToUpstream(() => api.commands.settled())\n transaction.after(() => api.commands.settled())\n return api\n }\n\n const api: AggregateRootApi<Name> & InternalAggregateRootApi = {\n type,\n withInitialState: (state) => {\n initialState = clone(state)\n return api as any\n },\n\n withCommands(handlers) {\n createCommands = (state, eventBuilder, options) => {\n if (state === null) {\n return (handlers as any)(eventBuilder, options)\n }\n\n return (handlers as any)(state, eventBuilder, options)\n }\n\n return api as any\n },\n\n withEvents(handler) {\n const eventMap: BaseEventMap =\n typeof handler === 'function'\n ? handler({ version: (version, schema) => ({ version, schema }), z })\n : handler\n\n for (const [type, config] of Object.entries(eventMap)) {\n const versions = Array.isArray(config) ? config : [config]\n for (const v of versions) {\n let schema: z.ZodType\n let version: number\n if (typeof v === 'object' && 'version' in v) {\n schema = v.schema\n version = v.version\n } else {\n schema = v\n version = 0\n }\n\n if (registeredEvents.get(type)?.has(version)) {\n throw new Error(\n `event ${type} is already registered in version ${version}`,\n )\n }\n\n if (!registeredEvents.has(type)) {\n registeredEvents.set(type, new Map())\n }\n registeredEvents.get(type)!.set(version, schema)\n }\n }\n return api as any\n },\n\n withEventHandlers(handler) {\n createEventHandlers = handler as any\n return api as any\n },\n\n withSnapshots: (handler) => {\n createSnapshotApi = handler as any\n return api as any\n },\n\n newStream(id) {\n if (!loadEvents) {\n throw new Error(\n `aggregate root ${type} is not linked to an event store`,\n )\n }\n\n const parentProjectionName = getProjectionInfo()?.name\n if (parentProjectionName) {\n throw new Error(\n `aggregateRoot.newStream() cannot be called inside a projection. Attempted on aggregate stream ${type} within Projection ${parentProjectionName}.`,\n )\n }\n return createStream(clone(initialState), id ?? crypto.randomUUID())\n .commands as any\n },\n\n loadStream(id, filter) {\n if (!loadEvents) {\n throw new Error(\n `aggregate root ${type} is not linked to an event store`,\n )\n }\n\n const parentProjectionName = getProjectionInfo()?.name\n if (parentProjectionName) {\n throw new Error(\n `aggregateRoot.loadStream(\"${id}\") cannot be called inside a projection. Attempted on aggregate stream ${type} within Projection ${parentProjectionName}.`,\n )\n }\n const root = createStream(clone(initialState), id)\n root.load(filter as any)\n return root.commands as any\n },\n\n linkEventStore(load, append, emit, trans, lock, snap) {\n loadEvents = load\n appendEvents = append\n emitEvents = emit\n transaction = trans\n createLock = lock\n snapshots = snap\n },\n get eventTypes() {\n return Array.from(registeredEvents.keys())\n },\n $inferEvents: null as never,\n $inferEvent: null as never,\n $inferState: null as never,\n $inferCommandParameters: null as never,\n }\n\n return api as Omit<\n AggregateRootApi<Name>,\n 'withCommands' | 'withEventHandlers'\n >\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "7975EF0D4D2A5E9964756E2164756E21",
9
+ "names": []
10
+ }
@@ -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
+ }