@requence/event-sourcing 1.4.1 → 1.5.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 (64) hide show
  1. package/README.md +6 -5
  2. package/dist/{chunk-yn2sjcg3.js → chunk-3251p253.js} +50 -1
  3. package/dist/chunk-3251p253.js.map +10 -0
  4. package/dist/{chunk-vep2byx4.js → chunk-7akf01zj.js} +11 -11
  5. package/dist/{chunk-vep2byx4.js.map → chunk-7akf01zj.js.map} +3 -3
  6. package/dist/{chunk-57b88rt5.js → chunk-8edc30gz.js} +11 -11
  7. package/dist/{chunk-57b88rt5.js.map → chunk-8edc30gz.js.map} +1 -1
  8. package/dist/chunk-ak901ckt.js +67 -0
  9. package/dist/chunk-ak901ckt.js.map +10 -0
  10. package/dist/{chunk-vvv14qxy.js → chunk-kqh3q3fj.js} +10 -8
  11. package/dist/{chunk-vvv14qxy.js.map → chunk-kqh3q3fj.js.map} +3 -3
  12. package/dist/{chunk-3ejt4h7x.js → chunk-zznvzvnv.js} +58 -46
  13. package/dist/chunk-zznvzvnv.js.map +10 -0
  14. package/dist/createAggregateRoot.d.ts.map +1 -1
  15. package/dist/createAggregateRoot.js +7 -7
  16. package/dist/createCheckpointApi.d.ts +30 -0
  17. package/dist/createCheckpointApi.d.ts.map +1 -1
  18. package/dist/createCheckpointApi.js +1 -1
  19. package/dist/createEventStore.js +8 -8
  20. package/dist/createProcessManager.d.ts.map +1 -1
  21. package/dist/createProcessManager.js +3 -3
  22. package/dist/createProjection.d.ts.map +1 -1
  23. package/dist/createProjection.js +2 -2
  24. package/dist/drizzle/index.js +14 -14
  25. package/dist/drizzle/index.js.map +1 -1
  26. package/dist/index.js +18 -18
  27. package/dist/index.js.map +1 -1
  28. package/dist/memory/index.d.ts +14 -3
  29. package/dist/memory/index.d.ts.map +1 -1
  30. package/dist/memory/index.js +37 -30
  31. package/dist/memory/index.js.map +3 -3
  32. package/dist/memory/index.spec.d.ts +2 -0
  33. package/dist/memory/index.spec.d.ts.map +1 -0
  34. package/dist/surreal/index.d.ts +35 -0
  35. package/dist/surreal/index.d.ts.map +1 -0
  36. package/dist/surreal/index.js +415 -0
  37. package/dist/surreal/index.js.map +10 -0
  38. package/dist/surreal/index.spec.d.ts +2 -0
  39. package/dist/surreal/index.spec.d.ts.map +1 -0
  40. package/dist/surreal/schema.d.ts +12 -0
  41. package/dist/surreal/schema.d.ts.map +1 -0
  42. package/dist/surreal/schema.js +12 -0
  43. package/dist/surreal/schema.js.map +9 -0
  44. package/dist/tests/projectionMultiInstance.spec.d.ts +2 -0
  45. package/dist/tests/projectionMultiInstance.spec.d.ts.map +1 -0
  46. package/dist/tests/storageDriverContract.d.ts +19 -0
  47. package/dist/tests/storageDriverContract.d.ts.map +1 -0
  48. package/package.json +19 -10
  49. package/src/createAggregateRoot.ts +2 -3
  50. package/src/createCheckpointApi.ts +98 -0
  51. package/src/createProcessManager.ts +7 -6
  52. package/src/createProjection.ts +91 -65
  53. package/src/memory/index.spec.ts +4 -0
  54. package/src/memory/index.ts +55 -29
  55. package/src/surreal/index.spec.ts +152 -0
  56. package/src/surreal/index.ts +604 -0
  57. package/src/surreal/schema.ts +68 -0
  58. package/src/tests/aggregateRoot.spec.ts +6 -3
  59. package/src/tests/eventStore.spec.ts +1 -3
  60. package/src/tests/processManagerMultiInstance.spec.ts +18 -7
  61. package/src/tests/projectionMultiInstance.spec.ts +283 -0
  62. package/src/tests/storageDriverContract.ts +612 -0
  63. package/dist/chunk-3ejt4h7x.js.map +0 -10
  64. package/dist/chunk-yn2sjcg3.js.map +0 -10
@@ -0,0 +1,415 @@
1
+ import {
2
+ SCHEMA_STATEMENTS,
3
+ TABLES,
4
+ initSchema
5
+ } from "../chunk-ak901ckt.js";
6
+ import {
7
+ isStreamEvents,
8
+ isStreamId
9
+ } from "../chunk-jd4q8fcf.js";
10
+ import {
11
+ createAggregateRoot
12
+ } from "../chunk-7akf01zj.js";
13
+ import {
14
+ createEventStore
15
+ } from "../chunk-8edc30gz.js";
16
+ import"../chunk-ty9bhdvx.js";
17
+ import"../chunk-z388cd25.js";
18
+ import"../chunk-24xjphpk.js";
19
+ import"../chunk-tfy01fah.js";
20
+ import"../chunk-zznvzvnv.js";
21
+ import"../chunk-8qn1nqnh.js";
22
+ import"../chunk-kqh3q3fj.js";
23
+ import"../chunk-6yrb30rx.js";
24
+ import {
25
+ extendTypes,
26
+ superjson
27
+ } from "../chunk-6q8mr1sg.js";
28
+ import"../chunk-2fe8jjdc.js";
29
+ import"../chunk-3251p253.js";
30
+ import"../chunk-pc1q4se9.js";
31
+ import {
32
+ ConcurrencyError
33
+ } from "../chunk-391m4mfp.js";
34
+
35
+ // src/surreal/index.ts
36
+ import { RecordId } from "surrealdb";
37
+ var CONFLICT_RETRIES = 10;
38
+ var CONCURRENCY_MARKER = "es_concurrency:";
39
+ var CHECKPOINT_CONFLICT_MARKER = "es_checkpoint_conflict";
40
+ function isThrownError(error) {
41
+ return typeof error === "object" && error !== null && error.kind === "Thrown";
42
+ }
43
+ function isAlreadyExistsError(error) {
44
+ return typeof error === "object" && error !== null && error.kind === "AlreadyExists";
45
+ }
46
+ function isUniqueIndexViolation(error) {
47
+ return error instanceof Error && /index .* already contains/i.test(error.message);
48
+ }
49
+ function isTransactionConflict(error) {
50
+ if (typeof error !== "object" || error === null) {
51
+ return false;
52
+ }
53
+ const serverError = error;
54
+ if (serverError.isTransactionConflict) {
55
+ return true;
56
+ }
57
+ return /read or write conflict|transaction can be retried/i.test(serverError.message ?? "");
58
+ }
59
+ function primaryError(errors) {
60
+ return errors.find((error) => !/was not executed/i.test(error.message)) ?? errors[0];
61
+ }
62
+ function toDate(value) {
63
+ if (value instanceof Date) {
64
+ return value;
65
+ }
66
+ const dateTime = value;
67
+ if (typeof dateTime?.toDate === "function") {
68
+ return dateTime.toDate();
69
+ }
70
+ return new Date(String(value));
71
+ }
72
+ function toOutputEvent(row) {
73
+ const { id: _id, ...event } = row;
74
+ return {
75
+ ...event,
76
+ position: Number(event.position),
77
+ streamVersion: Number(event.streamVersion),
78
+ createdAt: toDate(event.createdAt),
79
+ payload: superjson.parse(event.payload),
80
+ metadata: superjson.deserialize(event.metadata)
81
+ };
82
+ }
83
+ function conflictBackoff(attempt) {
84
+ const ms = Math.min(200, 5 * 2 ** attempt) * (0.5 + Math.random());
85
+ return new Promise((resolve) => setTimeout(resolve, ms));
86
+ }
87
+ var APPEND_QUERY = `
88
+ BEGIN;
89
+ LET $head = array::max(SELECT VALUE streamVersion FROM ${TABLES.events} WHERE streamId = $streamId AND streamType = $streamType) ?? 0;
90
+ IF $head != $expectedVersion { THROW "${CONCURRENCY_MARKER}" + <string>$head; };
91
+ LET $next = (UPSERT ONLY ${TABLES.counters}:${TABLES.events} SET value += $count RETURN VALUE value);
92
+ LET $base = $next - $count;
93
+ INSERT INTO ${TABLES.events} $events.map(|$event, $index| $event + { position: $base + $index + 1 });
94
+ COMMIT;
95
+ `;
96
+ var CHECKPOINT_UPSERT_QUERY = `
97
+ BEGIN;
98
+ LET $current = (SELECT VALUE version FROM ONLY $rid);
99
+ IF $current = NONE {
100
+ CREATE $rid CONTENT $content;
101
+ } ELSE IF $insertOnly = false AND $current = $expectedVersion {
102
+ UPDATE $rid MERGE $changes;
103
+ } ELSE {
104
+ THROW "${CHECKPOINT_CONFLICT_MARKER}";
105
+ };
106
+ COMMIT;
107
+ `;
108
+ function createSurrealStorage(params) {
109
+ const db = () => typeof params.database === "function" ? params.database() : params.database;
110
+ let schemaReady;
111
+ const ready = () => {
112
+ if (params.initSchema === false) {
113
+ return Promise.resolve();
114
+ }
115
+ schemaReady ??= initSchema(db());
116
+ return schemaReady;
117
+ };
118
+ async function runWithResponses(query, bindings) {
119
+ try {
120
+ const responses = await db().query(query, bindings).responses();
121
+ const errors = responses.filter((response) => !response.success).map((response) => response.error);
122
+ return { responses, errors };
123
+ } catch (error) {
124
+ if (isTransactionConflict(error)) {
125
+ return { responses: [], errors: [error] };
126
+ }
127
+ throw error;
128
+ }
129
+ }
130
+ return {
131
+ async* loadEvents(select, range) {
132
+ await ready();
133
+ const conditions = ["position > $lastPosition"];
134
+ const bindings = {};
135
+ if (select) {
136
+ const selects = Array.isArray(select) ? select : [select];
137
+ const streamTypes = selects.map((select2) => select2.stream);
138
+ const streamIds = selects.map((select2) => isStreamId(select2) ? select2.id : null).filter(Boolean);
139
+ const eventTypes = selects.flatMap((select2) => isStreamEvents(select2) ? select2.events : []);
140
+ conditions.push("streamType IN $streamTypes");
141
+ bindings.streamTypes = streamTypes;
142
+ if (streamIds.length > 0) {
143
+ conditions.push("streamId IN $streamIds");
144
+ bindings.streamIds = streamIds;
145
+ }
146
+ if (eventTypes.length > 0) {
147
+ conditions.push("type IN $eventTypes");
148
+ bindings.eventTypes = eventTypes;
149
+ }
150
+ }
151
+ let lastPosition = 0;
152
+ if (range?.from) {
153
+ lastPosition = range.from - 1;
154
+ }
155
+ const chunkSize = 50;
156
+ const query = `SELECT * FROM ${TABLES.events} WHERE ${conditions.join(" AND ")} ORDER BY position ASC LIMIT ${chunkSize};`;
157
+ while (true) {
158
+ const [resolvedEvents] = await db().query(query, { ...bindings, lastPosition }).collect();
159
+ if (resolvedEvents.length === 0) {
160
+ break;
161
+ }
162
+ for (const resolvedEvent of resolvedEvents) {
163
+ const position = Number(resolvedEvent.position);
164
+ if (range?.to && position > range.to) {
165
+ return;
166
+ }
167
+ yield toOutputEvent(resolvedEvent);
168
+ lastPosition = position;
169
+ }
170
+ }
171
+ },
172
+ async appendEvents(streamId, newEvents, expectedVersion) {
173
+ await ready();
174
+ const mappedEvents = newEvents.map((event, index) => ({
175
+ streamId: streamId.id,
176
+ streamType: streamId.stream,
177
+ streamVersion: expectedVersion + 1 + index,
178
+ type: event.type,
179
+ payload: superjson.stringify(event.payload),
180
+ metadata: superjson.serialize(event.metadata),
181
+ causationId: event.causationId ?? undefined,
182
+ actorId: event.actorId ?? undefined,
183
+ correlationId: event.correlationId ?? undefined,
184
+ schemaVersion: event.schemaVersion
185
+ }));
186
+ for (let attempt = 0;; attempt++) {
187
+ const { responses, errors } = await runWithResponses(APPEND_QUERY, {
188
+ streamId: streamId.id,
189
+ streamType: streamId.stream,
190
+ expectedVersion,
191
+ count: mappedEvents.length,
192
+ events: mappedEvents
193
+ });
194
+ if (errors.length === 0) {
195
+ const persistedEvents = responses.map((response) => response.success ? response.result : undefined).find(Array.isArray);
196
+ const resolvedEvents = persistedEvents.map(toOutputEvent).toSorted((eventA, eventB) => eventA.position - eventB.position);
197
+ params.onEventsAppended?.(resolvedEvents);
198
+ return resolvedEvents;
199
+ }
200
+ const concurrency = errors.find((error) => isThrownError(error) && error.message.includes(CONCURRENCY_MARKER));
201
+ if (concurrency) {
202
+ const currentVersion = Number(new RegExp(`${CONCURRENCY_MARKER}(\\d+)`).exec(concurrency.message)[1]);
203
+ throw new ConcurrencyError(streamId, expectedVersion, currentVersion);
204
+ }
205
+ if (errors.some(isUniqueIndexViolation)) {
206
+ const [version] = await db().query(`RETURN array::max(SELECT VALUE streamVersion FROM ${TABLES.events} WHERE streamId = $streamId AND streamType = $streamType) ?? 0;`, { streamId: streamId.id, streamType: streamId.stream }).collect();
207
+ const currentVersion = Number(version ?? 0);
208
+ throw new ConcurrencyError(streamId, expectedVersion, currentVersion);
209
+ }
210
+ if (errors.some(isTransactionConflict) && attempt < CONFLICT_RETRIES) {
211
+ await conflictBackoff(attempt);
212
+ continue;
213
+ }
214
+ throw primaryError(errors);
215
+ }
216
+ },
217
+ checkpoint: {
218
+ async upsert(checkpoint, expectedVersion) {
219
+ await ready();
220
+ const rid = new RecordId(TABLES.checkpoints, [
221
+ checkpoint.type,
222
+ checkpoint.name
223
+ ]);
224
+ const metadata = superjson.stringify(checkpoint.metadata);
225
+ const changes = {
226
+ lastEventPosition: checkpoint.lastEventPosition,
227
+ metadata,
228
+ version: checkpoint.version
229
+ };
230
+ for (let attempt = 0;; attempt++) {
231
+ const { errors } = await runWithResponses(CHECKPOINT_UPSERT_QUERY, {
232
+ rid,
233
+ content: {
234
+ type: checkpoint.type,
235
+ name: checkpoint.name,
236
+ ...changes
237
+ },
238
+ changes,
239
+ insertOnly: expectedVersion === null,
240
+ expectedVersion: expectedVersion ?? 0
241
+ });
242
+ if (errors.length === 0) {
243
+ return true;
244
+ }
245
+ if (errors.some((error) => isAlreadyExistsError(error) || isThrownError(error) && error.message.includes(CHECKPOINT_CONFLICT_MARKER))) {
246
+ return false;
247
+ }
248
+ if (errors.some(isTransactionConflict) && attempt < CONFLICT_RETRIES) {
249
+ await conflictBackoff(attempt);
250
+ continue;
251
+ }
252
+ throw primaryError(errors);
253
+ }
254
+ },
255
+ async get(type, name) {
256
+ await ready();
257
+ const rid = new RecordId(TABLES.checkpoints, [type, name]);
258
+ const [checkpoint] = await db().query(`SELECT * OMIT id FROM ONLY $rid;`, { rid }).collect();
259
+ if (!checkpoint) {
260
+ return null;
261
+ }
262
+ return {
263
+ type: checkpoint.type,
264
+ name: checkpoint.name,
265
+ lastEventPosition: Number(checkpoint.lastEventPosition),
266
+ version: Number(checkpoint.version),
267
+ metadata: superjson.parse(checkpoint.metadata)
268
+ };
269
+ },
270
+ async delete(type, name) {
271
+ await ready();
272
+ const rid = new RecordId(TABLES.checkpoints, [type, name]);
273
+ await db().query(`DELETE $rid;`, { rid }).collect();
274
+ }
275
+ },
276
+ projectionSnapshot: {
277
+ async put(snapshot) {
278
+ await ready();
279
+ await db().query(`CREATE ${TABLES.projectionSnapshots} CONTENT $content;`, {
280
+ content: {
281
+ projectionId: snapshot.projectionId,
282
+ projectionType: snapshot.projectionType,
283
+ lastEventPosition: snapshot.lastEventPosition,
284
+ schemaVersion: snapshot.schemaVersion,
285
+ state: superjson.stringify(snapshot.state),
286
+ metadata: superjson.stringify(snapshot.metadata)
287
+ }
288
+ }).collect();
289
+ },
290
+ async get(select) {
291
+ await ready();
292
+ const [[snapshot]] = await db().query(`SELECT * OMIT id FROM ${TABLES.projectionSnapshots} WHERE projectionId = $id AND projectionType = $projection ORDER BY lastEventPosition DESC LIMIT 1;`, { id: select.id, projection: select.projection }).collect();
293
+ if (!snapshot) {
294
+ return null;
295
+ }
296
+ return {
297
+ ...snapshot,
298
+ lastEventPosition: Number(snapshot.lastEventPosition),
299
+ schemaVersion: Number(snapshot.schemaVersion),
300
+ createdAt: toDate(snapshot.createdAt),
301
+ state: superjson.parse(snapshot.state),
302
+ metadata: superjson.parse(snapshot.metadata)
303
+ };
304
+ },
305
+ async delete(target, fromPosition) {
306
+ await ready();
307
+ const conditions = ["projectionType = $projection"];
308
+ const bindings = {
309
+ projection: target.projection
310
+ };
311
+ if (target.id) {
312
+ conditions.push("projectionId = $id");
313
+ bindings.id = target.id;
314
+ }
315
+ if (fromPosition) {
316
+ conditions.push("lastEventPosition > $fromPosition");
317
+ bindings.fromPosition = fromPosition;
318
+ }
319
+ await db().query(`DELETE ${TABLES.projectionSnapshots} WHERE ${conditions.join(" AND ")};`, bindings).collect();
320
+ },
321
+ async incrementAppliedCount(select) {
322
+ await ready();
323
+ for (let attempt = 0;; attempt++) {
324
+ try {
325
+ const [appliedEvents] = await db().query(`UPSERT ONLY ${TABLES.projectionAppliedEvents}:[$projection, $id] SET appliedEvents += 1 RETURN VALUE appliedEvents;`, { projection: select.projection, id: select.id }).collect();
326
+ return Number(appliedEvents);
327
+ } catch (error) {
328
+ if (isTransactionConflict(error) && attempt < CONFLICT_RETRIES) {
329
+ await conflictBackoff(attempt);
330
+ continue;
331
+ }
332
+ throw error;
333
+ }
334
+ }
335
+ }
336
+ },
337
+ aggregateRootSnapshot: {
338
+ async put(snapshot) {
339
+ await ready();
340
+ await db().query(`CREATE ${TABLES.aggregateRootSnapshots} CONTENT $content;`, {
341
+ content: {
342
+ streamId: snapshot.streamId,
343
+ streamType: snapshot.streamType,
344
+ streamVersion: snapshot.streamVersion,
345
+ lastEventPosition: snapshot.lastEventPosition,
346
+ schemaVersion: snapshot.schemaVersion,
347
+ state: superjson.stringify(snapshot.state),
348
+ metadata: superjson.stringify(snapshot.metadata)
349
+ }
350
+ }).collect();
351
+ },
352
+ async get(select) {
353
+ await ready();
354
+ const [[snapshot]] = await db().query(`SELECT * OMIT id FROM ${TABLES.aggregateRootSnapshots} WHERE streamId = $id AND streamType = $stream ORDER BY streamVersion DESC LIMIT 1;`, { id: select.id, stream: select.stream }).collect();
355
+ if (!snapshot) {
356
+ return null;
357
+ }
358
+ return {
359
+ ...snapshot,
360
+ streamVersion: Number(snapshot.streamVersion),
361
+ lastEventPosition: Number(snapshot.lastEventPosition),
362
+ schemaVersion: Number(snapshot.schemaVersion),
363
+ createdAt: toDate(snapshot.createdAt),
364
+ state: superjson.parse(snapshot.state),
365
+ metadata: superjson.parse(snapshot.metadata)
366
+ };
367
+ },
368
+ async delete(target, fromStreamVersion) {
369
+ await ready();
370
+ await db().query(`DELETE ${TABLES.aggregateRootSnapshots} WHERE streamId = $id AND streamType = $stream AND streamVersion > $fromStreamVersion;`, {
371
+ id: target.id,
372
+ stream: target.stream,
373
+ fromStreamVersion
374
+ }).collect();
375
+ }
376
+ }
377
+ };
378
+ }
379
+ function createEventStore2({
380
+ database,
381
+ aggregateRoots,
382
+ postProcessEvent,
383
+ onProjectionReplay,
384
+ onProcessManagerRefresh,
385
+ onEventsAppended,
386
+ autoInit,
387
+ initSchema: initSchema2,
388
+ lock
389
+ }) {
390
+ const storage = createSurrealStorage({
391
+ database,
392
+ initSchema: initSchema2,
393
+ onEventsAppended
394
+ });
395
+ return createEventStore({
396
+ aggregateRoots,
397
+ postProcessEvent,
398
+ onProjectionReplay,
399
+ onProcessManagerRefresh,
400
+ autoInit,
401
+ lock,
402
+ ...storage
403
+ });
404
+ }
405
+ export {
406
+ initSchema,
407
+ extendTypes,
408
+ createSurrealStorage,
409
+ createEventStore2 as createEventStore,
410
+ createAggregateRoot,
411
+ TABLES,
412
+ SCHEMA_STATEMENTS
413
+ };
414
+
415
+ //# debugId=F7D6DA35C7597AFE64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/surreal/index.ts"],
4
+ "sourcesContent": [
5
+ "import { RecordId, type Surreal } from 'surrealdb'\n\nimport type {\n AggregateRootSnapshotMethods,\n AnyAggregateRoot,\n} from '../createAggregateRoot.ts'\nimport type { CheckpointMethods } from '../createCheckpointApi.ts'\nimport type { ProjectionSnapshotMethods } from '../createProjection.ts'\nimport {\n ConcurrencyError,\n createEventStore as createBaseEventStore,\n isStreamEvents,\n isStreamId,\n} from '../index.ts'\nimport type {\n EventStore,\n EventStoreParamsWithAggregateRootSnapshots,\n EventsFromRoot,\n LoadEvents,\n OnProgress,\n} from '../createEventStore.ts'\nimport type { LockCreator } from '../lock.ts'\nimport { superjson } from '../superjson.ts'\nimport { TABLES, initSchema as runInitSchema } from './schema.ts'\nimport type {\n BaseInputEvent,\n BaseOutputEvent,\n MaybePromise,\n StreamId,\n} from '../utilityTypes.js'\n\nexport { createAggregateRoot } from '../index.ts'\nexport { extendTypes } from '../superjson.ts'\nexport { SCHEMA_STATEMENTS, TABLES, initSchema } from './schema.ts'\n\nexport type OnEventsAppended = (events: BaseOutputEvent[]) => MaybePromise<void>\n\ninterface SurrealEventStoreParams<Root extends AnyAggregateRoot> {\n database: Surreal | (() => Surreal)\n aggregateRoots: Root[]\n autoInit?: boolean\n initSchema?: boolean\n lock?: LockCreator\n postProcessEvent?: EventStoreParamsWithAggregateRootSnapshots<Root>['postProcessEvent']\n onProjectionReplay?: OnProgress\n onProcessManagerRefresh?: OnProgress\n onEventsAppended?: OnEventsAppended\n}\n\nconst CONFLICT_RETRIES = 10\nconst CONCURRENCY_MARKER = 'es_concurrency:'\nconst CHECKPOINT_CONFLICT_MARKER = 'es_checkpoint_conflict'\n\ntype QueryResponse =\n | { success: true; result: unknown }\n | { success: false; error: Error & { kind?: string } }\n\ntype ServerErrorLike = Error & {\n kind?: string\n isTransactionConflict?: boolean\n}\n\nfunction isThrownError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as ServerErrorLike).kind === 'Thrown'\n )\n}\n\nfunction isAlreadyExistsError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as ServerErrorLike).kind === 'AlreadyExists'\n )\n}\n\nfunction isUniqueIndexViolation(error: unknown): boolean {\n return (\n error instanceof Error && /index .* already contains/i.test(error.message)\n )\n}\n\n// Snapshot-isolation conflicts are safe to retry. SurrealDB >= 3.1 reports\n// them with a structured `TransactionConflict` detail; older servers only\n// expose the human-readable message.\nfunction isTransactionConflict(error: unknown): error is ServerErrorLike {\n if (typeof error !== 'object' || error === null) {\n return false\n }\n const serverError = error as ServerErrorLike\n if (serverError.isTransactionConflict) {\n return true\n }\n return /read or write conflict|transaction can be retried/i.test(\n serverError.message ?? '',\n )\n}\n\n// Statements after a failed one only report generic \"not executed\" errors —\n// surface the statement error that actually caused the transaction to fail.\nfunction primaryError(errors: ServerErrorLike[]): ServerErrorLike {\n return (\n errors.find((error) => !/was not executed/i.test(error.message)) ??\n errors[0]\n )\n}\n\nfunction toDate(value: unknown): Date {\n if (value instanceof Date) {\n return value\n }\n const dateTime = value as { toDate?: () => Date }\n if (typeof dateTime?.toDate === 'function') {\n return dateTime.toDate()\n }\n return new Date(String(value))\n}\n\nfunction toOutputEvent(row: any): BaseOutputEvent {\n const { id: _id, ...event } = row\n return {\n ...event,\n position: Number(event.position),\n streamVersion: Number(event.streamVersion),\n createdAt: toDate(event.createdAt),\n payload: superjson.parse(event.payload),\n metadata: superjson.deserialize(event.metadata),\n }\n}\n\n// Jittered exponential backoff so concurrent writers racing on the position\n// counter do not retry in lockstep.\nfunction conflictBackoff(attempt: number) {\n const ms = Math.min(200, 5 * 2 ** attempt) * (0.5 + Math.random())\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// The head version is computed with array::max instead of\n// ORDER BY ... DESC LIMIT 1: inside a transaction the index scan serves that\n// query in ascending order with the limit pushed down, returning the lowest\n// version instead of the highest.\nconst APPEND_QUERY = `\nBEGIN;\nLET $head = array::max(SELECT VALUE streamVersion FROM ${TABLES.events} WHERE streamId = $streamId AND streamType = $streamType) ?? 0;\nIF $head != $expectedVersion { THROW \"${CONCURRENCY_MARKER}\" + <string>$head; };\nLET $next = (UPSERT ONLY ${TABLES.counters}:${TABLES.events} SET value += $count RETURN VALUE value);\nLET $base = $next - $count;\nINSERT INTO ${TABLES.events} $events.map(|$event, $index| $event + { position: $base + $index + 1 });\nCOMMIT;\n`\n\nconst CHECKPOINT_UPSERT_QUERY = `\nBEGIN;\nLET $current = (SELECT VALUE version FROM ONLY $rid);\nIF $current = NONE {\n CREATE $rid CONTENT $content;\n} ELSE IF $insertOnly = false AND $current = $expectedVersion {\n UPDATE $rid MERGE $changes;\n} ELSE {\n THROW \"${CHECKPOINT_CONFLICT_MARKER}\";\n};\nCOMMIT;\n`\n\nexport function createSurrealStorage(params: {\n database: Surreal | (() => Surreal)\n initSchema?: boolean\n onEventsAppended?: OnEventsAppended\n}): {\n loadEvents: LoadEvents\n appendEvents: (\n streamId: StreamId,\n events: BaseInputEvent[],\n expectedVersion: number,\n ) => Promise<BaseOutputEvent[]>\n checkpoint: CheckpointMethods\n projectionSnapshot: ProjectionSnapshotMethods\n aggregateRootSnapshot: AggregateRootSnapshotMethods\n} {\n const db = () =>\n typeof params.database === 'function' ? params.database() : params.database\n\n let schemaReady: Promise<void> | undefined\n const ready = () => {\n if (params.initSchema === false) {\n return Promise.resolve()\n }\n schemaReady ??= runInitSchema(db())\n return schemaReady\n }\n\n // Statement failures come back as per-statement error responses, but\n // transaction conflicts reject the query promise itself — normalize both\n // into the errors array so the retry loops handle them uniformly.\n async function runWithResponses(\n query: string,\n bindings: Record<string, unknown>,\n ): Promise<{ responses: QueryResponse[]; errors: ServerErrorLike[] }> {\n try {\n const responses = (await db()\n .query(query, bindings)\n .responses()) as QueryResponse[]\n const errors = responses\n .filter((response) => !response.success)\n .map((response) => (response as { error: ServerErrorLike }).error)\n return { responses, errors }\n } catch (error) {\n if (isTransactionConflict(error)) {\n return { responses: [], errors: [error] }\n }\n throw error\n }\n }\n\n return {\n async *loadEvents(select, range) {\n await ready()\n const conditions = ['position > $lastPosition']\n const bindings: Record<string, unknown> = {}\n\n if (select) {\n const selects = Array.isArray(select) ? select : [select]\n const streamTypes = selects.map((select) => select.stream)\n const streamIds = selects\n .map((select) => (isStreamId(select) ? select.id : null))\n .filter(Boolean) as string[]\n const eventTypes = selects.flatMap((select) =>\n isStreamEvents(select) ? select.events : [],\n )\n conditions.push('streamType IN $streamTypes')\n bindings.streamTypes = streamTypes\n\n if (streamIds.length > 0) {\n conditions.push('streamId IN $streamIds')\n bindings.streamIds = streamIds\n }\n\n if (eventTypes.length > 0) {\n conditions.push('type IN $eventTypes')\n bindings.eventTypes = eventTypes\n }\n }\n let lastPosition = 0\n\n if (range?.from) {\n lastPosition = range.from - 1\n }\n\n // range.to is applied client-side: combining both bounds on the\n // uniquely indexed position field trips a SurrealDB 3.0 planner bug\n // where the exclusive lower bound is ignored.\n const chunkSize = 50\n const query = `SELECT * FROM ${TABLES.events} WHERE ${conditions.join(' AND ')} ORDER BY position ASC LIMIT ${chunkSize};`\n\n while (true) {\n const [resolvedEvents] = (await db()\n .query(query, { ...bindings, lastPosition })\n .collect()) as [any[]]\n\n if (resolvedEvents.length === 0) {\n break\n }\n\n for (const resolvedEvent of resolvedEvents) {\n const position = Number(resolvedEvent.position)\n if (range?.to && position > range.to) {\n return\n }\n yield toOutputEvent(resolvedEvent)\n lastPosition = position\n }\n }\n },\n\n async appendEvents(streamId, newEvents, expectedVersion) {\n await ready()\n const mappedEvents = newEvents.map((event, index) => ({\n streamId: streamId.id,\n streamType: streamId.stream,\n streamVersion: expectedVersion + 1 + index,\n type: event.type,\n payload: superjson.stringify(event.payload),\n metadata: superjson.serialize(event.metadata),\n causationId: event.causationId ?? undefined,\n actorId: event.actorId ?? undefined,\n correlationId: event.correlationId ?? undefined,\n schemaVersion: event.schemaVersion,\n }))\n\n for (let attempt = 0; ; attempt++) {\n const { responses, errors } = await runWithResponses(APPEND_QUERY, {\n streamId: streamId.id,\n streamType: streamId.stream,\n expectedVersion,\n count: mappedEvents.length,\n events: mappedEvents,\n })\n\n if (errors.length === 0) {\n const persistedEvents = responses\n .map((response) => (response.success ? response.result : undefined))\n .find(Array.isArray) as any[]\n\n const resolvedEvents = persistedEvents\n .map(toOutputEvent)\n .toSorted((eventA, eventB) => eventA.position - eventB.position)\n\n params.onEventsAppended?.(resolvedEvents)\n\n return resolvedEvents\n }\n\n const concurrency = errors.find(\n (error) =>\n isThrownError(error) && error.message.includes(CONCURRENCY_MARKER),\n )\n if (concurrency) {\n const currentVersion = Number(\n new RegExp(`${CONCURRENCY_MARKER}(\\\\d+)`).exec(\n concurrency.message,\n )![1],\n )\n throw new ConcurrencyError(streamId, expectedVersion, currentVersion)\n }\n\n if (errors.some(isUniqueIndexViolation)) {\n const [version] = (await db()\n .query(\n `RETURN array::max(SELECT VALUE streamVersion FROM ${TABLES.events} WHERE streamId = $streamId AND streamType = $streamType) ?? 0;`,\n { streamId: streamId.id, streamType: streamId.stream },\n )\n .collect()) as [number]\n\n const currentVersion = Number(version ?? 0)\n throw new ConcurrencyError(streamId, expectedVersion, currentVersion)\n }\n\n if (errors.some(isTransactionConflict) && attempt < CONFLICT_RETRIES) {\n await conflictBackoff(attempt)\n continue\n }\n\n throw primaryError(errors)\n }\n },\n\n checkpoint: {\n async upsert(checkpoint, expectedVersion) {\n await ready()\n const rid = new RecordId(TABLES.checkpoints, [\n checkpoint.type,\n checkpoint.name,\n ])\n const metadata = superjson.stringify(checkpoint.metadata)\n const changes = {\n lastEventPosition: checkpoint.lastEventPosition,\n metadata,\n version: checkpoint.version,\n }\n\n // Compare-and-swap: create when no checkpoint exists, otherwise only\n // apply when the stored version still matches `expectedVersion`. A\n // `null` expectation means \"insert only\" — an existing record is a\n // conflict.\n for (let attempt = 0; ; attempt++) {\n const { errors } = await runWithResponses(CHECKPOINT_UPSERT_QUERY, {\n rid,\n content: {\n type: checkpoint.type,\n name: checkpoint.name,\n ...changes,\n },\n changes,\n insertOnly: expectedVersion === null,\n expectedVersion: expectedVersion ?? 0,\n })\n\n if (errors.length === 0) {\n return true\n }\n\n if (\n errors.some(\n (error) =>\n isAlreadyExistsError(error) ||\n (isThrownError(error) &&\n error.message.includes(CHECKPOINT_CONFLICT_MARKER)),\n )\n ) {\n return false\n }\n\n if (\n errors.some(isTransactionConflict) &&\n attempt < CONFLICT_RETRIES\n ) {\n await conflictBackoff(attempt)\n continue\n }\n\n throw primaryError(errors)\n }\n },\n async get(type, name) {\n await ready()\n const rid = new RecordId(TABLES.checkpoints, [type, name])\n const [checkpoint] = (await db()\n .query(`SELECT * OMIT id FROM ONLY $rid;`, { rid })\n .collect()) as [any]\n\n if (!checkpoint) {\n return null\n }\n\n return {\n type: checkpoint.type,\n name: checkpoint.name,\n lastEventPosition: Number(checkpoint.lastEventPosition),\n version: Number(checkpoint.version),\n metadata: superjson.parse(checkpoint.metadata),\n }\n },\n async delete(type, name) {\n await ready()\n const rid = new RecordId(TABLES.checkpoints, [type, name])\n await db().query(`DELETE $rid;`, { rid }).collect()\n },\n },\n\n projectionSnapshot: {\n async put(snapshot) {\n await ready()\n await db()\n .query(`CREATE ${TABLES.projectionSnapshots} CONTENT $content;`, {\n content: {\n projectionId: snapshot.projectionId,\n projectionType: snapshot.projectionType,\n lastEventPosition: snapshot.lastEventPosition,\n schemaVersion: snapshot.schemaVersion,\n state: superjson.stringify(snapshot.state),\n metadata: superjson.stringify(snapshot.metadata),\n },\n })\n .collect()\n },\n async get(select) {\n await ready()\n const [[snapshot]] = (await db()\n .query(\n `SELECT * OMIT id FROM ${TABLES.projectionSnapshots} WHERE projectionId = $id AND projectionType = $projection ORDER BY lastEventPosition DESC LIMIT 1;`,\n { id: select.id, projection: select.projection },\n )\n .collect()) as [any[]]\n\n if (!snapshot) {\n return null\n }\n\n return {\n ...snapshot,\n lastEventPosition: Number(snapshot.lastEventPosition),\n schemaVersion: Number(snapshot.schemaVersion),\n createdAt: toDate(snapshot.createdAt),\n state: superjson.parse(snapshot.state),\n metadata: superjson.parse(snapshot.metadata),\n }\n },\n async delete(target, fromPosition) {\n await ready()\n const conditions = ['projectionType = $projection']\n const bindings: Record<string, unknown> = {\n projection: target.projection,\n }\n\n if (target.id) {\n conditions.push('projectionId = $id')\n bindings.id = target.id\n }\n\n if (fromPosition) {\n conditions.push('lastEventPosition > $fromPosition')\n bindings.fromPosition = fromPosition\n }\n\n await db()\n .query(\n `DELETE ${TABLES.projectionSnapshots} WHERE ${conditions.join(' AND ')};`,\n bindings,\n )\n .collect()\n },\n async incrementAppliedCount(select) {\n await ready()\n for (let attempt = 0; ; attempt++) {\n try {\n const [appliedEvents] = (await db()\n .query(\n `UPSERT ONLY ${TABLES.projectionAppliedEvents}:[$projection, $id] SET appliedEvents += 1 RETURN VALUE appliedEvents;`,\n { projection: select.projection, id: select.id },\n )\n .collect()) as [number]\n\n return Number(appliedEvents)\n } catch (error) {\n if (isTransactionConflict(error) && attempt < CONFLICT_RETRIES) {\n await conflictBackoff(attempt)\n continue\n }\n throw error\n }\n }\n },\n },\n\n aggregateRootSnapshot: {\n async put(snapshot) {\n await ready()\n await db()\n .query(`CREATE ${TABLES.aggregateRootSnapshots} CONTENT $content;`, {\n content: {\n streamId: snapshot.streamId,\n streamType: snapshot.streamType,\n streamVersion: snapshot.streamVersion,\n lastEventPosition: snapshot.lastEventPosition,\n schemaVersion: snapshot.schemaVersion,\n state: superjson.stringify(snapshot.state),\n metadata: superjson.stringify(snapshot.metadata),\n },\n })\n .collect()\n },\n async get(select) {\n await ready()\n const [[snapshot]] = (await db()\n .query(\n `SELECT * OMIT id FROM ${TABLES.aggregateRootSnapshots} WHERE streamId = $id AND streamType = $stream ORDER BY streamVersion DESC LIMIT 1;`,\n { id: select.id, stream: select.stream },\n )\n .collect()) as [any[]]\n\n if (!snapshot) {\n return null\n }\n\n return {\n ...snapshot,\n streamVersion: Number(snapshot.streamVersion),\n lastEventPosition: Number(snapshot.lastEventPosition),\n schemaVersion: Number(snapshot.schemaVersion),\n createdAt: toDate(snapshot.createdAt),\n state: superjson.parse(snapshot.state),\n metadata: superjson.parse(snapshot.metadata),\n }\n },\n async delete(target, fromStreamVersion) {\n await ready()\n await db()\n .query(\n `DELETE ${TABLES.aggregateRootSnapshots} WHERE streamId = $id AND streamType = $stream AND streamVersion > $fromStreamVersion;`,\n {\n id: target.id,\n stream: target.stream,\n fromStreamVersion,\n },\n )\n .collect()\n },\n },\n }\n}\n\nexport function createEventStore<const Root extends AnyAggregateRoot>({\n database,\n aggregateRoots,\n postProcessEvent,\n onProjectionReplay,\n onProcessManagerRefresh,\n onEventsAppended,\n autoInit,\n initSchema,\n lock,\n}: SurrealEventStoreParams<Root>): EventStore<\n EventsFromRoot<Root>,\n Root,\n true\n> {\n const storage = createSurrealStorage({\n database,\n initSchema,\n onEventsAppended,\n })\n\n return createBaseEventStore({\n aggregateRoots,\n postProcessEvent,\n onProjectionReplay,\n onProcessManagerRefresh,\n autoInit,\n lock,\n ...storage,\n }) as any\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAiDA,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAC3B,IAAM,6BAA6B;AAWnC,SAAS,aAAa,CAAC,OAAyB;AAAA,EAC9C,OACE,OAAO,UAAU,YACjB,UAAU,QACT,MAA0B,SAAS;AAAA;AAIxC,SAAS,oBAAoB,CAAC,OAAyB;AAAA,EACrD,OACE,OAAO,UAAU,YACjB,UAAU,QACT,MAA0B,SAAS;AAAA;AAIxC,SAAS,sBAAsB,CAAC,OAAyB;AAAA,EACvD,OACE,iBAAiB,SAAS,6BAA6B,KAAK,MAAM,OAAO;AAAA;AAO7E,SAAS,qBAAqB,CAAC,OAA0C;AAAA,EACvE,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAAA,IAC/C,OAAO;AAAA,EACT;AAAA,EACA,MAAM,cAAc;AAAA,EACpB,IAAI,YAAY,uBAAuB;AAAA,IACrC,OAAO;AAAA,EACT;AAAA,EACA,OAAO,qDAAqD,KAC1D,YAAY,WAAW,EACzB;AAAA;AAKF,SAAS,YAAY,CAAC,QAA4C;AAAA,EAChE,OACE,OAAO,KAAK,CAAC,UAAU,CAAC,oBAAoB,KAAK,MAAM,OAAO,CAAC,KAC/D,OAAO;AAAA;AAIX,SAAS,MAAM,CAAC,OAAsB;AAAA,EACpC,IAAI,iBAAiB,MAAM;AAAA,IACzB,OAAO;AAAA,EACT;AAAA,EACA,MAAM,WAAW;AAAA,EACjB,IAAI,OAAO,UAAU,WAAW,YAAY;AAAA,IAC1C,OAAO,SAAS,OAAO;AAAA,EACzB;AAAA,EACA,OAAO,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA;AAG/B,SAAS,aAAa,CAAC,KAA2B;AAAA,EAChD,QAAQ,IAAI,QAAQ,UAAU;AAAA,EAC9B,OAAO;AAAA,OACF;AAAA,IACH,UAAU,OAAO,MAAM,QAAQ;AAAA,IAC/B,eAAe,OAAO,MAAM,aAAa;AAAA,IACzC,WAAW,OAAO,MAAM,SAAS;AAAA,IACjC,SAAS,UAAU,MAAM,MAAM,OAAO;AAAA,IACtC,UAAU,UAAU,YAAY,MAAM,QAAQ;AAAA,EAChD;AAAA;AAKF,SAAS,eAAe,CAAC,SAAiB;AAAA,EACxC,MAAM,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO;AAAA,EAChE,OAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA;AAOzD,IAAM,eAAe;AAAA;AAAA,yDAEoC,OAAO;AAAA,wCACxB;AAAA,2BACb,OAAO,YAAY,OAAO;AAAA;AAAA,cAEvC,OAAO;AAAA;AAAA;AAIrB,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAQrB;AAAA;AAAA;AAAA;AAKJ,SAAS,oBAAoB,CAAC,QAcnC;AAAA,EACA,MAAM,KAAK,MACT,OAAO,OAAO,aAAa,aAAa,OAAO,SAAS,IAAI,OAAO;AAAA,EAErE,IAAI;AAAA,EACJ,MAAM,QAAQ,MAAM;AAAA,IAClB,IAAI,OAAO,eAAe,OAAO;AAAA,MAC/B,OAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,IACA,gBAAgB,WAAc,GAAG,CAAC;AAAA,IAClC,OAAO;AAAA;AAAA,EAMT,eAAe,gBAAgB,CAC7B,OACA,UACoE;AAAA,IACpE,IAAI;AAAA,MACF,MAAM,YAAa,MAAM,GAAG,EACzB,MAAM,OAAO,QAAQ,EACrB,UAAU;AAAA,MACb,MAAM,SAAS,UACZ,OAAO,CAAC,aAAa,CAAC,SAAS,OAAO,EACtC,IAAI,CAAC,aAAc,SAAwC,KAAK;AAAA,MACnE,OAAO,EAAE,WAAW,OAAO;AAAA,MAC3B,OAAO,OAAO;AAAA,MACd,IAAI,sBAAsB,KAAK,GAAG;AAAA,QAChC,OAAO,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE;AAAA,MAC1C;AAAA,MACA,MAAM;AAAA;AAAA;AAAA,EAIV,OAAO;AAAA,WACE,UAAU,CAAC,QAAQ,OAAO;AAAA,MAC/B,MAAM,MAAM;AAAA,MACZ,MAAM,aAAa,CAAC,0BAA0B;AAAA,MAC9C,MAAM,WAAoC,CAAC;AAAA,MAE3C,IAAI,QAAQ;AAAA,QACV,MAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAAA,QACxD,MAAM,cAAc,QAAQ,IAAI,CAAC,YAAW,QAAO,MAAM;AAAA,QACzD,MAAM,YAAY,QACf,IAAI,CAAC,YAAY,WAAW,OAAM,IAAI,QAAO,KAAK,IAAK,EACvD,OAAO,OAAO;AAAA,QACjB,MAAM,aAAa,QAAQ,QAAQ,CAAC,YAClC,eAAe,OAAM,IAAI,QAAO,SAAS,CAAC,CAC5C;AAAA,QACA,WAAW,KAAK,4BAA4B;AAAA,QAC5C,SAAS,cAAc;AAAA,QAEvB,IAAI,UAAU,SAAS,GAAG;AAAA,UACxB,WAAW,KAAK,wBAAwB;AAAA,UACxC,SAAS,YAAY;AAAA,QACvB;AAAA,QAEA,IAAI,WAAW,SAAS,GAAG;AAAA,UACzB,WAAW,KAAK,qBAAqB;AAAA,UACrC,SAAS,aAAa;AAAA,QACxB;AAAA,MACF;AAAA,MACA,IAAI,eAAe;AAAA,MAEnB,IAAI,OAAO,MAAM;AAAA,QACf,eAAe,MAAM,OAAO;AAAA,MAC9B;AAAA,MAKA,MAAM,YAAY;AAAA,MAClB,MAAM,QAAQ,iBAAiB,OAAO,gBAAgB,WAAW,KAAK,OAAO,iCAAiC;AAAA,MAE9G,OAAO,MAAM;AAAA,QACX,OAAO,kBAAmB,MAAM,GAAG,EAChC,MAAM,OAAO,KAAK,UAAU,aAAa,CAAC,EAC1C,QAAQ;AAAA,QAEX,IAAI,eAAe,WAAW,GAAG;AAAA,UAC/B;AAAA,QACF;AAAA,QAEA,WAAW,iBAAiB,gBAAgB;AAAA,UAC1C,MAAM,WAAW,OAAO,cAAc,QAAQ;AAAA,UAC9C,IAAI,OAAO,MAAM,WAAW,MAAM,IAAI;AAAA,YACpC;AAAA,UACF;AAAA,UACA,MAAM,cAAc,aAAa;AAAA,UACjC,eAAe;AAAA,QACjB;AAAA,MACF;AAAA;AAAA,SAGI,aAAY,CAAC,UAAU,WAAW,iBAAiB;AAAA,MACvD,MAAM,MAAM;AAAA,MACZ,MAAM,eAAe,UAAU,IAAI,CAAC,OAAO,WAAW;AAAA,QACpD,UAAU,SAAS;AAAA,QACnB,YAAY,SAAS;AAAA,QACrB,eAAe,kBAAkB,IAAI;AAAA,QACrC,MAAM,MAAM;AAAA,QACZ,SAAS,UAAU,UAAU,MAAM,OAAO;AAAA,QAC1C,UAAU,UAAU,UAAU,MAAM,QAAQ;AAAA,QAC5C,aAAa,MAAM,eAAe;AAAA,QAClC,SAAS,MAAM,WAAW;AAAA,QAC1B,eAAe,MAAM,iBAAiB;AAAA,QACtC,eAAe,MAAM;AAAA,MACvB,EAAE;AAAA,MAEF,SAAS,UAAU,IAAK,WAAW;AAAA,QACjC,QAAQ,WAAW,WAAW,MAAM,iBAAiB,cAAc;AAAA,UACjE,UAAU,SAAS;AAAA,UACnB,YAAY,SAAS;AAAA,UACrB;AAAA,UACA,OAAO,aAAa;AAAA,UACpB,QAAQ;AAAA,QACV,CAAC;AAAA,QAED,IAAI,OAAO,WAAW,GAAG;AAAA,UACvB,MAAM,kBAAkB,UACrB,IAAI,CAAC,aAAc,SAAS,UAAU,SAAS,SAAS,SAAU,EAClE,KAAK,MAAM,OAAO;AAAA,UAErB,MAAM,iBAAiB,gBACpB,IAAI,aAAa,EACjB,SAAS,CAAC,QAAQ,WAAW,OAAO,WAAW,OAAO,QAAQ;AAAA,UAEjE,OAAO,mBAAmB,cAAc;AAAA,UAExC,OAAO;AAAA,QACT;AAAA,QAEA,MAAM,cAAc,OAAO,KACzB,CAAC,UACC,cAAc,KAAK,KAAK,MAAM,QAAQ,SAAS,kBAAkB,CACrE;AAAA,QACA,IAAI,aAAa;AAAA,UACf,MAAM,iBAAiB,OACrB,IAAI,OAAO,GAAG,0BAA0B,EAAE,KACxC,YAAY,OACd,EAAG,EACL;AAAA,UACA,MAAM,IAAI,iBAAiB,UAAU,iBAAiB,cAAc;AAAA,QACtE;AAAA,QAEA,IAAI,OAAO,KAAK,sBAAsB,GAAG;AAAA,UACvC,OAAO,WAAY,MAAM,GAAG,EACzB,MACC,qDAAqD,OAAO,yEAC5D,EAAE,UAAU,SAAS,IAAI,YAAY,SAAS,OAAO,CACvD,EACC,QAAQ;AAAA,UAEX,MAAM,iBAAiB,OAAO,WAAW,CAAC;AAAA,UAC1C,MAAM,IAAI,iBAAiB,UAAU,iBAAiB,cAAc;AAAA,QACtE;AAAA,QAEA,IAAI,OAAO,KAAK,qBAAqB,KAAK,UAAU,kBAAkB;AAAA,UACpE,MAAM,gBAAgB,OAAO;AAAA,UAC7B;AAAA,QACF;AAAA,QAEA,MAAM,aAAa,MAAM;AAAA,MAC3B;AAAA;AAAA,IAGF,YAAY;AAAA,WACJ,OAAM,CAAC,YAAY,iBAAiB;AAAA,QACxC,MAAM,MAAM;AAAA,QACZ,MAAM,MAAM,IAAI,SAAS,OAAO,aAAa;AAAA,UAC3C,WAAW;AAAA,UACX,WAAW;AAAA,QACb,CAAC;AAAA,QACD,MAAM,WAAW,UAAU,UAAU,WAAW,QAAQ;AAAA,QACxD,MAAM,UAAU;AAAA,UACd,mBAAmB,WAAW;AAAA,UAC9B;AAAA,UACA,SAAS,WAAW;AAAA,QACtB;AAAA,QAMA,SAAS,UAAU,IAAK,WAAW;AAAA,UACjC,QAAQ,WAAW,MAAM,iBAAiB,yBAAyB;AAAA,YACjE;AAAA,YACA,SAAS;AAAA,cACP,MAAM,WAAW;AAAA,cACjB,MAAM,WAAW;AAAA,iBACd;AAAA,YACL;AAAA,YACA;AAAA,YACA,YAAY,oBAAoB;AAAA,YAChC,iBAAiB,mBAAmB;AAAA,UACtC,CAAC;AAAA,UAED,IAAI,OAAO,WAAW,GAAG;AAAA,YACvB,OAAO;AAAA,UACT;AAAA,UAEA,IACE,OAAO,KACL,CAAC,UACC,qBAAqB,KAAK,KACzB,cAAc,KAAK,KAClB,MAAM,QAAQ,SAAS,0BAA0B,CACvD,GACA;AAAA,YACA,OAAO;AAAA,UACT;AAAA,UAEA,IACE,OAAO,KAAK,qBAAqB,KACjC,UAAU,kBACV;AAAA,YACA,MAAM,gBAAgB,OAAO;AAAA,YAC7B;AAAA,UACF;AAAA,UAEA,MAAM,aAAa,MAAM;AAAA,QAC3B;AAAA;AAAA,WAEI,IAAG,CAAC,MAAM,MAAM;AAAA,QACpB,MAAM,MAAM;AAAA,QACZ,MAAM,MAAM,IAAI,SAAS,OAAO,aAAa,CAAC,MAAM,IAAI,CAAC;AAAA,QACzD,OAAO,cAAe,MAAM,GAAG,EAC5B,MAAM,oCAAoC,EAAE,IAAI,CAAC,EACjD,QAAQ;AAAA,QAEX,IAAI,CAAC,YAAY;AAAA,UACf,OAAO;AAAA,QACT;AAAA,QAEA,OAAO;AAAA,UACL,MAAM,WAAW;AAAA,UACjB,MAAM,WAAW;AAAA,UACjB,mBAAmB,OAAO,WAAW,iBAAiB;AAAA,UACtD,SAAS,OAAO,WAAW,OAAO;AAAA,UAClC,UAAU,UAAU,MAAM,WAAW,QAAQ;AAAA,QAC/C;AAAA;AAAA,WAEI,OAAM,CAAC,MAAM,MAAM;AAAA,QACvB,MAAM,MAAM;AAAA,QACZ,MAAM,MAAM,IAAI,SAAS,OAAO,aAAa,CAAC,MAAM,IAAI,CAAC;AAAA,QACzD,MAAM,GAAG,EAAE,MAAM,gBAAgB,EAAE,IAAI,CAAC,EAAE,QAAQ;AAAA;AAAA,IAEtD;AAAA,IAEA,oBAAoB;AAAA,WACZ,IAAG,CAAC,UAAU;AAAA,QAClB,MAAM,MAAM;AAAA,QACZ,MAAM,GAAG,EACN,MAAM,UAAU,OAAO,yCAAyC;AAAA,UAC/D,SAAS;AAAA,YACP,cAAc,SAAS;AAAA,YACvB,gBAAgB,SAAS;AAAA,YACzB,mBAAmB,SAAS;AAAA,YAC5B,eAAe,SAAS;AAAA,YACxB,OAAO,UAAU,UAAU,SAAS,KAAK;AAAA,YACzC,UAAU,UAAU,UAAU,SAAS,QAAQ;AAAA,UACjD;AAAA,QACF,CAAC,EACA,QAAQ;AAAA;AAAA,WAEP,IAAG,CAAC,QAAQ;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,QAAQ,aAAc,MAAM,GAAG,EAC5B,MACC,yBAAyB,OAAO,0HAChC,EAAE,IAAI,OAAO,IAAI,YAAY,OAAO,WAAW,CACjD,EACC,QAAQ;AAAA,QAEX,IAAI,CAAC,UAAU;AAAA,UACb,OAAO;AAAA,QACT;AAAA,QAEA,OAAO;AAAA,aACF;AAAA,UACH,mBAAmB,OAAO,SAAS,iBAAiB;AAAA,UACpD,eAAe,OAAO,SAAS,aAAa;AAAA,UAC5C,WAAW,OAAO,SAAS,SAAS;AAAA,UACpC,OAAO,UAAU,MAAM,SAAS,KAAK;AAAA,UACrC,UAAU,UAAU,MAAM,SAAS,QAAQ;AAAA,QAC7C;AAAA;AAAA,WAEI,OAAM,CAAC,QAAQ,cAAc;AAAA,QACjC,MAAM,MAAM;AAAA,QACZ,MAAM,aAAa,CAAC,8BAA8B;AAAA,QAClD,MAAM,WAAoC;AAAA,UACxC,YAAY,OAAO;AAAA,QACrB;AAAA,QAEA,IAAI,OAAO,IAAI;AAAA,UACb,WAAW,KAAK,oBAAoB;AAAA,UACpC,SAAS,KAAK,OAAO;AAAA,QACvB;AAAA,QAEA,IAAI,cAAc;AAAA,UAChB,WAAW,KAAK,mCAAmC;AAAA,UACnD,SAAS,eAAe;AAAA,QAC1B;AAAA,QAEA,MAAM,GAAG,EACN,MACC,UAAU,OAAO,6BAA6B,WAAW,KAAK,OAAO,MACrE,QACF,EACC,QAAQ;AAAA;AAAA,WAEP,sBAAqB,CAAC,QAAQ;AAAA,QAClC,MAAM,MAAM;AAAA,QACZ,SAAS,UAAU,IAAK,WAAW;AAAA,UACjC,IAAI;AAAA,YACF,OAAO,iBAAkB,MAAM,GAAG,EAC/B,MACC,eAAe,OAAO,iGACtB,EAAE,YAAY,OAAO,YAAY,IAAI,OAAO,GAAG,CACjD,EACC,QAAQ;AAAA,YAEX,OAAO,OAAO,aAAa;AAAA,YAC3B,OAAO,OAAO;AAAA,YACd,IAAI,sBAAsB,KAAK,KAAK,UAAU,kBAAkB;AAAA,cAC9D,MAAM,gBAAgB,OAAO;AAAA,cAC7B;AAAA,YACF;AAAA,YACA,MAAM;AAAA;AAAA,QAEV;AAAA;AAAA,IAEJ;AAAA,IAEA,uBAAuB;AAAA,WACf,IAAG,CAAC,UAAU;AAAA,QAClB,MAAM,MAAM;AAAA,QACZ,MAAM,GAAG,EACN,MAAM,UAAU,OAAO,4CAA4C;AAAA,UAClE,SAAS;AAAA,YACP,UAAU,SAAS;AAAA,YACnB,YAAY,SAAS;AAAA,YACrB,eAAe,SAAS;AAAA,YACxB,mBAAmB,SAAS;AAAA,YAC5B,eAAe,SAAS;AAAA,YACxB,OAAO,UAAU,UAAU,SAAS,KAAK;AAAA,YACzC,UAAU,UAAU,UAAU,SAAS,QAAQ;AAAA,UACjD;AAAA,QACF,CAAC,EACA,QAAQ;AAAA;AAAA,WAEP,IAAG,CAAC,QAAQ;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,QAAQ,aAAc,MAAM,GAAG,EAC5B,MACC,yBAAyB,OAAO,6GAChC,EAAE,IAAI,OAAO,IAAI,QAAQ,OAAO,OAAO,CACzC,EACC,QAAQ;AAAA,QAEX,IAAI,CAAC,UAAU;AAAA,UACb,OAAO;AAAA,QACT;AAAA,QAEA,OAAO;AAAA,aACF;AAAA,UACH,eAAe,OAAO,SAAS,aAAa;AAAA,UAC5C,mBAAmB,OAAO,SAAS,iBAAiB;AAAA,UACpD,eAAe,OAAO,SAAS,aAAa;AAAA,UAC5C,WAAW,OAAO,SAAS,SAAS;AAAA,UACpC,OAAO,UAAU,MAAM,SAAS,KAAK;AAAA,UACrC,UAAU,UAAU,MAAM,SAAS,QAAQ;AAAA,QAC7C;AAAA;AAAA,WAEI,OAAM,CAAC,QAAQ,mBAAmB;AAAA,QACtC,MAAM,MAAM;AAAA,QACZ,MAAM,GAAG,EACN,MACC,UAAU,OAAO,gHACjB;AAAA,UACE,IAAI,OAAO;AAAA,UACX,QAAQ,OAAO;AAAA,UACf;AAAA,QACF,CACF,EACC,QAAQ;AAAA;AAAA,IAEf;AAAA,EACF;AAAA;AAGK,SAAS,iBAAqD;AAAA,EACnE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,GAKA;AAAA,EACA,MAAM,UAAU,qBAAqB;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA,EAED,OAAO,iBAAqB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,OACG;AAAA,EACL,CAAC;AAAA;",
8
+ "debugId": "F7D6DA35C7597AFE64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.spec.d.ts","sourceRoot":"","sources":["../../src/surreal/index.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { Surreal } from 'surrealdb';
2
+ export declare const TABLES: {
3
+ readonly events: "events";
4
+ readonly counters: "event_sourcing_counters";
5
+ readonly checkpoints: "checkpoints";
6
+ readonly aggregateRootSnapshots: "aggregate_root_snapshots";
7
+ readonly projectionSnapshots: "projection_snapshots";
8
+ readonly projectionAppliedEvents: "projection_applied_events";
9
+ };
10
+ export declare const SCHEMA_STATEMENTS: string;
11
+ export declare function initSchema(database: Surreal): Promise<void>;
12
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/surreal/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,eAAO,MAAM,MAAM;;;;;;;CAOT,CAAA;AAIV,eAAO,MAAM,iBAAiB,QAkD7B,CAAA;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE"}
@@ -0,0 +1,12 @@
1
+ import {
2
+ SCHEMA_STATEMENTS,
3
+ TABLES,
4
+ initSchema
5
+ } from "../chunk-ak901ckt.js";
6
+ export {
7
+ initSchema,
8
+ TABLES,
9
+ SCHEMA_STATEMENTS
10
+ };
11
+
12
+ //# debugId=CB35243CA3B02CFF64756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "CB35243CA3B02CFF64756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=projectionMultiInstance.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectionMultiInstance.spec.d.ts","sourceRoot":"","sources":["../../src/tests/projectionMultiInstance.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import type { AggregateRootSnapshotMethods } from '../createAggregateRoot.ts';
2
+ import type { CheckpointMethods } from '../createCheckpointApi.ts';
3
+ import type { LoadEvents } from '../createEventStore.ts';
4
+ import type { ProjectionSnapshotMethods } from '../createProjection.ts';
5
+ import type { BaseInputEvent, BaseOutputEvent, MaybePromise, StreamId } from '../utilityTypes.ts';
6
+ export type StorageDriver = {
7
+ loadEvents: LoadEvents;
8
+ appendEvents: (streamId: StreamId, events: BaseInputEvent[], expectedVersion: number) => MaybePromise<BaseOutputEvent[]>;
9
+ checkpoint: CheckpointMethods;
10
+ projectionSnapshot: ProjectionSnapshotMethods;
11
+ aggregateRootSnapshot: AggregateRootSnapshotMethods;
12
+ };
13
+ /**
14
+ * Reusable contract suite exercising the raw storage methods every driver
15
+ * must supply to the core `createEventStore`. Call it once per driver with a
16
+ * factory producing a fresh, empty storage instance.
17
+ */
18
+ export declare function runStorageDriverContract(name: string, makeStorage: () => MaybePromise<StorageDriver>): void;
19
+ //# sourceMappingURL=storageDriverContract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storageDriverContract.d.ts","sourceRoot":"","sources":["../../src/tests/storageDriverContract.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,4BAA4B,EAC7B,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAEV,yBAAyB,EAC1B,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,QAAQ,EACT,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,UAAU,CAAA;IACtB,YAAY,EAAE,CACZ,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,cAAc,EAAE,EACxB,eAAe,EAAE,MAAM,KACpB,YAAY,CAAC,eAAe,EAAE,CAAC,CAAA;IACpC,UAAU,EAAE,iBAAiB,CAAA;IAC7B,kBAAkB,EAAE,yBAAyB,CAAA;IAC7C,qBAAqB,EAAE,4BAA4B,CAAA;CACpD,CAAA;AA8CD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,YAAY,CAAC,aAAa,CAAC,QAghB/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@requence/event-sourcing",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "Event sourcing primitives — aggregate roots, projections, process managers, and event listeners",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -37,6 +37,12 @@
37
37
  "types": "./dist/redis/index.d.ts",
38
38
  "import": "./dist/redis/index.js",
39
39
  "default": "./dist/redis/index.js"
40
+ },
41
+ "./surreal": {
42
+ "bun": "./src/surreal/index.ts",
43
+ "types": "./dist/surreal/index.d.ts",
44
+ "import": "./dist/surreal/index.js",
45
+ "default": "./dist/surreal/index.js"
40
46
  }
41
47
  },
42
48
  "files": [
@@ -49,6 +55,9 @@
49
55
  "build": "bun run build.ts",
50
56
  "test": "bun test",
51
57
  "check-types": "tsc --noEmit",
58
+ "lint": "oxlint",
59
+ "format": "oxfmt",
60
+ "format:check": "oxfmt --check",
52
61
  "cs": "changeset",
53
62
  "version": "changeset version",
54
63
  "release": "bun run build && changeset publish",
@@ -62,7 +71,8 @@
62
71
  "peerDependencies": {
63
72
  "zod": "^4.3.6",
64
73
  "drizzle-orm": "^0.45.1",
65
- "ioredis": "^5.9.3"
74
+ "ioredis": "^5.9.3",
75
+ "surrealdb": "^2.0.0"
66
76
  },
67
77
  "peerDependenciesMeta": {
68
78
  "drizzle-orm": {
@@ -70,24 +80,23 @@
70
80
  },
71
81
  "ioredis": {
72
82
  "optional": true
83
+ },
84
+ "surrealdb": {
85
+ "optional": true
73
86
  }
74
87
  },
75
88
  "devDependencies": {
76
89
  "@changesets/changelog-github": "^0.6.0",
77
90
  "@changesets/cli": "^2.31.0",
78
- "@eslint/js": "^10.0.1",
79
- "@stylistic/eslint-plugin": "^5.10.0",
91
+ "@surrealdb/node": "^3.0.3",
80
92
  "@types/bun": "^1.3.13",
81
93
  "@types/node": "^25.6.0",
82
94
  "drizzle-orm": "^0.45.1",
83
- "eslint": "^10.2.1",
84
- "eslint-plugin-import": "^2.32.0",
85
- "eslint-plugin-no-only-tests": "^3.3.0",
86
- "globals": "^17.5.0",
87
95
  "ioredis": "^5.10.1",
88
- "prettier": "^3.8.3",
96
+ "oxfmt": "^0.59.0",
97
+ "oxlint": "^1.74.0",
98
+ "surrealdb": "^2.0.7",
89
99
  "typescript": "^6.0.3",
90
- "typescript-eslint": "^8.59.1",
91
100
  "zod": "^4.3.6"
92
101
  },
93
102
  "license": "MIT",
@@ -616,9 +616,8 @@ export function createAggregateRoot<Name extends string>(type: Name) {
616
616
  return previousEvents
617
617
  }
618
618
  const events = (
619
- await normalizeArray(
620
- commandFn.apply(rawCommands, args),
621
- () => extendLock(),
619
+ await normalizeArray(commandFn.apply(rawCommands, args), () =>
620
+ extendLock(),
622
621
  )
623
622
  ).filter((event) => Boolean(event))
624
623