@loomwork/event-runtime 0.0.0-stage → 0.1.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 (153) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +95 -2
  4. package/dist/BranchRegistry.d.ts +140 -0
  5. package/dist/BranchRegistry.d.ts.map +1 -0
  6. package/dist/BranchRegistry.js +66 -0
  7. package/dist/BranchRegistry.js.map +1 -0
  8. package/dist/CoalescingEventStore.d.ts +97 -0
  9. package/dist/CoalescingEventStore.d.ts.map +1 -0
  10. package/dist/CoalescingEventStore.js +180 -0
  11. package/dist/CoalescingEventStore.js.map +1 -0
  12. package/dist/InMemoryArchiveStore.d.ts +19 -0
  13. package/dist/InMemoryArchiveStore.d.ts.map +1 -0
  14. package/dist/InMemoryArchiveStore.js +49 -0
  15. package/dist/InMemoryArchiveStore.js.map +1 -0
  16. package/dist/InMemoryAsyncEventStore.d.ts +111 -0
  17. package/dist/InMemoryAsyncEventStore.d.ts.map +1 -0
  18. package/dist/InMemoryAsyncEventStore.js +374 -0
  19. package/dist/InMemoryAsyncEventStore.js.map +1 -0
  20. package/dist/InMemoryBranchRegistry.d.ts +31 -0
  21. package/dist/InMemoryBranchRegistry.d.ts.map +1 -0
  22. package/dist/InMemoryBranchRegistry.js +167 -0
  23. package/dist/InMemoryBranchRegistry.js.map +1 -0
  24. package/dist/ProfilingEventStore.d.ts +75 -0
  25. package/dist/ProfilingEventStore.d.ts.map +1 -0
  26. package/dist/ProfilingEventStore.js +133 -0
  27. package/dist/ProfilingEventStore.js.map +1 -0
  28. package/dist/ShardPlan.d.ts +87 -0
  29. package/dist/ShardPlan.d.ts.map +1 -0
  30. package/dist/ShardPlan.js +193 -0
  31. package/dist/ShardPlan.js.map +1 -0
  32. package/dist/ShardedEventStore.d.ts +149 -0
  33. package/dist/ShardedEventStore.d.ts.map +1 -0
  34. package/dist/ShardedEventStore.js +468 -0
  35. package/dist/ShardedEventStore.js.map +1 -0
  36. package/dist/SubscriberRegistry.d.ts +59 -0
  37. package/dist/SubscriberRegistry.d.ts.map +1 -0
  38. package/dist/SubscriberRegistry.js +113 -0
  39. package/dist/SubscriberRegistry.js.map +1 -0
  40. package/dist/TenantScopedArchiveStore.d.ts +36 -0
  41. package/dist/TenantScopedArchiveStore.d.ts.map +1 -0
  42. package/dist/TenantScopedArchiveStore.js +96 -0
  43. package/dist/TenantScopedArchiveStore.js.map +1 -0
  44. package/dist/TenantScopedEventStore.d.ts +59 -0
  45. package/dist/TenantScopedEventStore.d.ts.map +1 -0
  46. package/dist/TenantScopedEventStore.js +227 -0
  47. package/dist/TenantScopedEventStore.js.map +1 -0
  48. package/dist/archive.d.ts +68 -0
  49. package/dist/archive.d.ts.map +1 -0
  50. package/dist/archive.js +207 -0
  51. package/dist/archive.js.map +1 -0
  52. package/dist/archiveSpi.d.ts +165 -0
  53. package/dist/archiveSpi.d.ts.map +1 -0
  54. package/dist/archiveSpi.js +184 -0
  55. package/dist/archiveSpi.js.map +1 -0
  56. package/dist/creationMarker.d.ts +13 -0
  57. package/dist/creationMarker.d.ts.map +1 -0
  58. package/dist/creationMarker.js +17 -0
  59. package/dist/creationMarker.js.map +1 -0
  60. package/dist/decoratorPlan.typecheck.d.ts +2 -0
  61. package/dist/decoratorPlan.typecheck.d.ts.map +1 -0
  62. package/dist/decoratorPlan.typecheck.js +41 -0
  63. package/dist/decoratorPlan.typecheck.js.map +1 -0
  64. package/dist/eventKindClass.d.ts +40 -0
  65. package/dist/eventKindClass.d.ts.map +1 -0
  66. package/dist/eventKindClass.js +99 -0
  67. package/dist/eventKindClass.js.map +1 -0
  68. package/dist/executionIdOrder.d.ts +21 -0
  69. package/dist/executionIdOrder.d.ts.map +1 -0
  70. package/dist/executionIdOrder.js +43 -0
  71. package/dist/executionIdOrder.js.map +1 -0
  72. package/dist/fencing.d.ts +21 -0
  73. package/dist/fencing.d.ts.map +1 -0
  74. package/dist/fencing.js +36 -0
  75. package/dist/fencing.js.map +1 -0
  76. package/dist/index.d.ts +39 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +126 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/installOptional.d.ts +52 -0
  81. package/dist/installOptional.d.ts.map +1 -0
  82. package/dist/installOptional.js +112 -0
  83. package/dist/installOptional.js.map +1 -0
  84. package/dist/keyUnavailable.d.ts +67 -0
  85. package/dist/keyUnavailable.d.ts.map +1 -0
  86. package/dist/keyUnavailable.js +87 -0
  87. package/dist/keyUnavailable.js.map +1 -0
  88. package/dist/leases.d.ts +177 -0
  89. package/dist/leases.d.ts.map +1 -0
  90. package/dist/leases.js +142 -0
  91. package/dist/leases.js.map +1 -0
  92. package/dist/payloadShredded.d.ts +30 -0
  93. package/dist/payloadShredded.d.ts.map +1 -0
  94. package/dist/payloadShredded.js +67 -0
  95. package/dist/payloadShredded.js.map +1 -0
  96. package/dist/scaleDecorators.bench.d.ts +2 -0
  97. package/dist/scaleDecorators.bench.d.ts.map +1 -0
  98. package/dist/scaleDecorators.bench.js +93 -0
  99. package/dist/scaleDecorators.bench.js.map +1 -0
  100. package/dist/sequenceRange.bench.d.ts +2 -0
  101. package/dist/sequenceRange.bench.d.ts.map +1 -0
  102. package/dist/sequenceRange.bench.js +46 -0
  103. package/dist/sequenceRange.bench.js.map +1 -0
  104. package/dist/sequenceRange.d.ts +10 -0
  105. package/dist/sequenceRange.d.ts.map +1 -0
  106. package/dist/sequenceRange.js +35 -0
  107. package/dist/sequenceRange.js.map +1 -0
  108. package/dist/timers.d.ts +87 -0
  109. package/dist/timers.d.ts.map +1 -0
  110. package/dist/timers.js +68 -0
  111. package/dist/timers.js.map +1 -0
  112. package/dist/types.d.ts +634 -0
  113. package/dist/types.d.ts.map +1 -0
  114. package/dist/types.js +429 -0
  115. package/dist/types.js.map +1 -0
  116. package/dist/writeConflicts.d.ts +46 -0
  117. package/dist/writeConflicts.d.ts.map +1 -0
  118. package/dist/writeConflicts.js +63 -0
  119. package/dist/writeConflicts.js.map +1 -0
  120. package/memory.d.ts +4 -0
  121. package/memory.js +13 -0
  122. package/package.json +67 -3
  123. package/sequence.d.ts +1 -0
  124. package/sequence.js +5 -0
  125. package/src/BranchRegistry.ts +161 -0
  126. package/src/CoalescingEventStore.ts +214 -0
  127. package/src/InMemoryArchiveStore.ts +69 -0
  128. package/src/InMemoryAsyncEventStore.ts +425 -0
  129. package/src/InMemoryBranchRegistry.ts +178 -0
  130. package/src/ProfilingEventStore.ts +153 -0
  131. package/src/ShardPlan.ts +235 -0
  132. package/src/ShardedEventStore.ts +557 -0
  133. package/src/SubscriberRegistry.ts +115 -0
  134. package/src/TenantScopedArchiveStore.ts +114 -0
  135. package/src/TenantScopedEventStore.ts +266 -0
  136. package/src/archive.ts +248 -0
  137. package/src/archiveSpi.ts +308 -0
  138. package/src/creationMarker.ts +15 -0
  139. package/src/decoratorPlan.typecheck.ts +52 -0
  140. package/src/eventKindClass.ts +127 -0
  141. package/src/executionIdOrder.ts +38 -0
  142. package/src/fencing.ts +31 -0
  143. package/src/index.ts +151 -0
  144. package/src/installOptional.ts +136 -0
  145. package/src/keyUnavailable.ts +98 -0
  146. package/src/leases.ts +319 -0
  147. package/src/payloadShredded.ts +63 -0
  148. package/src/scaleDecorators.bench.ts +107 -0
  149. package/src/sequenceRange.bench.ts +51 -0
  150. package/src/sequenceRange.ts +35 -0
  151. package/src/timers.ts +150 -0
  152. package/src/types.ts +857 -0
  153. package/src/writeConflicts.ts +56 -0
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CoalescingEventStore = void 0;
4
+ const installOptional_1 = require("./installOptional");
5
+ const types_1 = require("./types");
6
+ /**
7
+ * Merges concurrent appends **across executions** into one `appendBatch` (ADR 0074 D2).
8
+ *
9
+ * Why a decorator and not the journal: `ExecutionJournal` is per-execution, so a
10
+ * journal-side batcher can only ever hold one execution's envelopes — a 1-step flow emits
11
+ * four, and with a realistic slow effect the serial tail drains between `STEP_STARTED` and
12
+ * `STEP_COMPLETED`, so it collects one. Measured; it is ADR 0065's own gate-2 case. A store
13
+ * decorator sees every live execution at once and can actually fill a batch.
14
+ *
15
+ * **Ordering.** Each `ExecutionJournal` still serializes its own execution (D3 unchanged) —
16
+ * it awaits each `append` before issuing the next, so this store never holds two envelopes
17
+ * of one execution concurrently through the normal path. Should it anyway (a second writer,
18
+ * a caller not using the journal), the queue is FIFO and `appendBatch` is specified to
19
+ * apply in array order, so arrival order is preserved regardless.
20
+ *
21
+ * **Durability.** An `append` promise resolves only once its batch has committed. Nothing
22
+ * resolves early, so every `drain()` barrier keeps exactly the meaning it has today. This
23
+ * is the property that distinguishes coalescing from the durability classes ADR 0065
24
+ * rejected on invariants.
25
+ *
26
+ * **Failure.** A batch rejection rejects every caller in that batch, so a partial write can
27
+ * never look like success. Combined with `appendBatch`'s all-or-nothing contract, a failed
28
+ * flush leaves the log untouched and every writer informed — **but only when the inner store
29
+ * implements `appendBatch`**. Over a store without it, `appendBatchOf` falls back to a
30
+ * sequential `append` loop, which is not atomic: envelopes ahead of the failing one are
31
+ * already durable while their writers are told the batch failed. Every decorator between
32
+ * this one and the backend must therefore carry `appendBatch` down (ADR 0074 Amendment —
33
+ * `runEventStoreDecoratorConformance` checks it).
34
+ *
35
+ * **Blast radius.** One stale-fence zombie in a batch rejects the whole batch, so every
36
+ * healthy writer coalesced with it is told "failed" too (nothing of theirs lands; they abort
37
+ * and recovery re-drives them from the log). Coalescing widens a fencing rejection from one
38
+ * execution to up to `maxBatch` of them — a price worth paying while fencing is rare.
39
+ *
40
+ * Inert unless wired: compose it explicitly in `storage`, outermost-but-one (sharding
41
+ * outside it, since routing must pick a shard before a batch is formed for that shard).
42
+ */
43
+ class CoalescingEventStore {
44
+ inner;
45
+ head;
46
+ readRange;
47
+ acquireFence;
48
+ acquireFenceIf;
49
+ appendIfUnchanged;
50
+ storeNow;
51
+ sealingCapability;
52
+ readAtRest;
53
+ copyPrefix;
54
+ deleteExecution;
55
+ deleteThrough;
56
+ truncatePrefix;
57
+ truncatePrefixKeeping;
58
+ listExecutions;
59
+ listExecutionsPage;
60
+ maxBatch;
61
+ maxLingerMs;
62
+ queue = [];
63
+ flushScheduled = false;
64
+ timer;
65
+ /** Serializes flushes so two batches can never be in flight against the inner store. */
66
+ tail = Promise.resolve();
67
+ closed = false;
68
+ constructor(inner, options = {}) {
69
+ this.inner = inner;
70
+ this.maxBatch = Math.max(1, options.maxBatch ?? 16);
71
+ this.maxLingerMs = Math.max(0, options.maxLingerMs ?? 0);
72
+ // Pass through every optional capability the inner store has, so wrapping never
73
+ // silently downgrades a backend (a missing `readRange` would push callers onto the
74
+ // slow fallback for no reason). `appendBatch` is this class's own method — batching is
75
+ // what it is for — so the plan covers every other optional method.
76
+ const plan = {
77
+ head: "forward",
78
+ readRange: "forward",
79
+ acquireFence: "forward",
80
+ acquireFenceIf: "forward",
81
+ appendIfUnchanged: "forward",
82
+ storeNow: "forward",
83
+ sealingCapability: "forward",
84
+ readAtRest: "forward",
85
+ copyPrefix: "forward",
86
+ deleteExecution: "forward",
87
+ deleteThrough: "forward",
88
+ truncatePrefix: "forward",
89
+ truncatePrefixKeeping: "forward",
90
+ listExecutions: "forward",
91
+ listExecutionsPage: "forward",
92
+ };
93
+ (0, installOptional_1.installOptional)(this, inner, plan);
94
+ }
95
+ append(envelope) {
96
+ if (this.closed)
97
+ return Promise.reject(new Error("CoalescingEventStore is closed"));
98
+ return new Promise((resolve, reject) => {
99
+ this.queue.push({ envelope, resolve, reject });
100
+ if (this.queue.length >= this.maxBatch)
101
+ this.flushNow();
102
+ else
103
+ this.scheduleFlush();
104
+ });
105
+ }
106
+ /**
107
+ * A caller that already has a batch skips the queue entirely — it is by definition
108
+ * already coalesced, and threading it through the queue would only risk splitting it.
109
+ */
110
+ appendBatch(envelopes) {
111
+ if (envelopes.length === 0)
112
+ return Promise.resolve();
113
+ if (this.closed)
114
+ return Promise.reject(new Error("CoalescingEventStore is closed"));
115
+ return this.enqueueFlush(() => (0, types_1.appendBatchOf)(this.inner, envelopes));
116
+ }
117
+ scheduleFlush() {
118
+ if (this.flushScheduled)
119
+ return;
120
+ this.flushScheduled = true;
121
+ if (this.maxLingerMs === 0) {
122
+ // Next microtask: collects everything enqueued in this turn — which under concurrency
123
+ // is the whole in-flight set — without deferring to a timer.
124
+ queueMicrotask(() => this.flushNow());
125
+ }
126
+ else {
127
+ this.timer = setTimeout(() => this.flushNow(), this.maxLingerMs);
128
+ }
129
+ }
130
+ flushNow() {
131
+ if (this.timer !== undefined) {
132
+ clearTimeout(this.timer);
133
+ this.timer = undefined;
134
+ }
135
+ this.flushScheduled = false;
136
+ if (this.queue.length === 0)
137
+ return;
138
+ const batch = this.queue;
139
+ this.queue = [];
140
+ void this.enqueueFlush(async () => {
141
+ await (0, types_1.appendBatchOf)(this.inner, batch.map((p) => p.envelope));
142
+ }).then(() => {
143
+ for (const p of batch)
144
+ p.resolve();
145
+ }, (err) => {
146
+ // Every writer in a failed batch learns it failed — a partial write must never be
147
+ // reported as success.
148
+ for (const p of batch)
149
+ p.reject(err);
150
+ });
151
+ }
152
+ /** Chain onto the serial tail so batches commit one at a time, in enqueue order. */
153
+ enqueueFlush(run) {
154
+ const result = this.tail.then(run);
155
+ // The tail itself must never reject, or every later flush would inherit the failure.
156
+ this.tail = result.then(() => undefined, () => undefined);
157
+ return result;
158
+ }
159
+ read(executionId) {
160
+ return this.inner.read(executionId);
161
+ }
162
+ readFrom(executionId, fromSequence) {
163
+ return this.inner.readFrom(executionId, fromSequence);
164
+ }
165
+ subscribe(executionId, handler) {
166
+ return this.inner.subscribe(executionId, handler);
167
+ }
168
+ /** Flush anything queued and wait for in-flight batches. Safe to call repeatedly. */
169
+ async drain() {
170
+ this.flushNow();
171
+ await this.tail;
172
+ }
173
+ async close() {
174
+ await this.drain();
175
+ this.closed = true;
176
+ await this.inner.close();
177
+ }
178
+ }
179
+ exports.CoalescingEventStore = CoalescingEventStore;
180
+ //# sourceMappingURL=CoalescingEventStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CoalescingEventStore.js","sourceRoot":"","sources":["../src/CoalescingEventStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { type ArchiveExecutionPage, type ArchiveSegment, type ArchiveSegmentHeader, type ArchiveStore } from "./archiveSpi";
2
+ /** In-memory {@link ArchiveStore}: for tests, the memory storage kind and in-memory event stores. */
3
+ export declare class InMemoryArchiveStore implements ArchiveStore {
4
+ private readonly byExecution;
5
+ append(executionId: string, segment: ArchiveSegment): Promise<void>;
6
+ headers(executionId: string): Promise<ArchiveSegmentHeader[]>;
7
+ listExecutions(opts?: {
8
+ cursor?: string;
9
+ limit?: number;
10
+ }): Promise<ArchiveExecutionPage>;
11
+ read(executionId: string, range?: {
12
+ readonly from?: number;
13
+ readonly through?: number;
14
+ }): Promise<ArchiveSegment[]>;
15
+ replaceSegments(executionId: string, expected: readonly ArchiveSegmentHeader[], next: readonly ArchiveSegment[]): Promise<void>;
16
+ deleteExecution(executionId: string): Promise<void>;
17
+ close(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=InMemoryArchiveStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryArchiveStore.d.ts","sourceRoot":"","sources":["../src/InMemoryArchiveStore.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAIlB,MAAM,cAAc,CAAC;AAGtB,qGAAqG;AACrG,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuC;IAE7D,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAQnE,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAI7D,cAAc,CAClB,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC7C,OAAO,CAAC,oBAAoB,CAAC;IAQ1B,IAAI,CACR,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAChE,OAAO,CAAC,cAAc,EAAE,CAAC;IAUtB,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,IAAI,EAAE,SAAS,cAAc,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;IAQV,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAC7B"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemoryArchiveStore = void 0;
4
+ const archive_1 = require("./archive");
5
+ const archiveSpi_1 = require("./archiveSpi");
6
+ const executionIdOrder_1 = require("./executionIdOrder");
7
+ /** In-memory {@link ArchiveStore}: for tests, the memory storage kind and in-memory event stores. */
8
+ class InMemoryArchiveStore {
9
+ byExecution = new Map();
10
+ async append(executionId, segment) {
11
+ const stored = this.byExecution.get(executionId) ?? [];
12
+ if ((0, archiveSpi_1.classifyAppend)(executionId, stored, segment) === "noop")
13
+ return;
14
+ // A copy through JSON, as a durable backend would hold it: the caller's objects never alias.
15
+ const copy = JSON.parse(JSON.stringify(segment));
16
+ this.byExecution.set(executionId, [...stored, copy]);
17
+ }
18
+ async headers(executionId) {
19
+ return (this.byExecution.get(executionId) ?? []).map(archiveSpi_1.headerOf);
20
+ }
21
+ async listExecutions(opts = {}) {
22
+ const ids = [...this.byExecution.entries()]
23
+ .filter(([, segs]) => segs.length > 0)
24
+ .map(([id]) => id)
25
+ .sort(executionIdOrder_1.compareExecutionIds);
26
+ return (0, archive_1.pageIds)(ids, opts);
27
+ }
28
+ async read(executionId, range = {}) {
29
+ return (this.byExecution.get(executionId) ?? [])
30
+ .filter((s) => (range.from === undefined || s.through >= range.from) &&
31
+ (range.through === undefined || s.from <= range.through))
32
+ .map((s) => JSON.parse(JSON.stringify(s)));
33
+ }
34
+ async replaceSegments(executionId, expected, next) {
35
+ const stored = this.byExecution.get(executionId) ?? [];
36
+ (0, archiveSpi_1.assertReplaceable)(executionId, stored.map(archiveSpi_1.headerOf), expected, next);
37
+ const copy = JSON.parse(JSON.stringify(next));
38
+ if (copy.length === 0)
39
+ this.byExecution.delete(executionId);
40
+ else
41
+ this.byExecution.set(executionId, copy);
42
+ }
43
+ async deleteExecution(executionId) {
44
+ this.byExecution.delete(executionId);
45
+ }
46
+ async close() { }
47
+ }
48
+ exports.InMemoryArchiveStore = InMemoryArchiveStore;
49
+ //# sourceMappingURL=InMemoryArchiveStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryArchiveStore.js","sourceRoot":"","sources":["../src/InMemoryArchiveStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import type { EventEnvelope } from "@loomwork/core";
2
+ import type { AcquireFenceIfResult, AppendIfUnchangedResult, EventStore, TipObservation } from "./types";
3
+ export interface InMemoryAsyncEventStoreOptions {
4
+ /** Artificial per-operation delay (ms) to model network latency. Default 0. */
5
+ latencyMs?: number;
6
+ /**
7
+ * If provided and it returns true for an envelope, `append` rejects with the
8
+ * given/derived error instead of storing it — for exercising the durable-write
9
+ * failure path. Default: never fails.
10
+ */
11
+ failAppendWhen?: (envelope: EventEnvelope) => boolean | string | Error;
12
+ }
13
+ /**
14
+ * Reference **async** EventStore: a genuine Promise-returning backend (unlike the
15
+ * synchronous SQLite/in-memory stores, which merely satisfy the async interface).
16
+ * Use it as a starting point for a real network backend, or in tests to exercise
17
+ * the runtime's async write pipeline, ordering, and failure handling.
18
+ */
19
+ export declare class InMemoryAsyncEventStore implements EventStore {
20
+ private readonly log;
21
+ /**
22
+ * The executionIds in SPI order (code point), built lazily by `listExecutionsPage` and
23
+ * dropped only when the SET of executions changes (a new one, or a delete). A full paged
24
+ * walk is then one O(n log n) sort plus O(log n) per page, not a re-sort per page.
25
+ */
26
+ private sortedIds;
27
+ /**
28
+ * #309 — every envelope id in the store, the index behind the `id` PRIMARY KEY both SQL
29
+ * backends have. GLOBAL, not per-execution, because that is what the SQL constraint is:
30
+ * two executions may not share an envelope id either.
31
+ *
32
+ * It has to be maintained everywhere the log changes, not just on append — a stale entry
33
+ * after a delete would refuse a legitimate re-append forever, and an un-removed one is a
34
+ * leak in a store whose whole job is to be re-created cheaply in tests. The removal side
35
+ * is only correct BECAUSE the invariant holds: an id belongs to exactly one envelope, so
36
+ * dropping the ids of the events being dropped cannot orphan someone else's claim.
37
+ *
38
+ * Costs one hash-table slot per event; the id strings themselves are already retained by
39
+ * the envelopes in `log`, so this is not a second copy of the data.
40
+ */
41
+ private readonly ids;
42
+ private readonly subscribers;
43
+ private readonly fences;
44
+ /**
45
+ * ADR 0129 — the store-assigned incarnation of each execution, taken from a store-wide
46
+ * monotonic counter when its sequence 0 first lands and dropped with the log. Store metadata
47
+ * only: never in an envelope, a fold, a snapshot or a hash, so replay stays byte-identical.
48
+ */
49
+ private readonly incarnations;
50
+ private nextIncarnation;
51
+ private readonly latencyMs;
52
+ private readonly failAppendWhen;
53
+ constructor(options?: InMemoryAsyncEventStoreOptions);
54
+ private tick;
55
+ append(envelope: EventEnvelope): Promise<void>;
56
+ /**
57
+ * ADR 0074 D1 — the reference implementation of the `appendBatch` contract, and what a
58
+ * bring-your-own backend can read to see what it has to honour.
59
+ *
60
+ * One `tick()` for the whole batch, because the point of the method is one round-trip.
61
+ * Atomicity is why validation is a separate pass: EVERY envelope is checked before ANY
62
+ * is committed, so a fenced-out or rejected envelope leaves the log exactly as it was
63
+ * rather than half-applied. Fencing is checked per envelope, since a batch may span
64
+ * executions.
65
+ */
66
+ appendBatch(envelopes: readonly EventEnvelope[]): Promise<void>;
67
+ /**
68
+ * Throws if this envelope may not land: a duplicate `(executionId, sequence)`, a
69
+ * duplicate envelope `id`, K9.4 fencing, or an injected failure.
70
+ */
71
+ private assertAppendable;
72
+ /** Insert into the execution's log, keeping it sorted by sequence. */
73
+ private commit;
74
+ read(executionId: string): Promise<EventEnvelope[]>;
75
+ readFrom(executionId: string, fromSequence: number): Promise<EventEnvelope[]>;
76
+ head(executionId: string): Promise<number | null>;
77
+ readRange(executionId: string, fromSequence: number, toSequence: number): Promise<EventEnvelope[]>;
78
+ copyPrefix(fromExecutionId: string, toExecutionId: string, throughSequence: number, metadata: Record<string, unknown>): Promise<void>;
79
+ acquireFence(executionId: string): Promise<number>;
80
+ /**
81
+ * ADR 0118 D1 — one synchronous step after the tick, so it cannot interleave with an append's
82
+ * own check-and-commit (which is also synchronous after ITS tick): the token compare-and-set
83
+ * plus a scan of the tail past `parkSeq`, which is short (the events appended since the park).
84
+ */
85
+ acquireFenceIf(executionId: string, expect: {
86
+ fence: number;
87
+ parkSeq: number;
88
+ }): Promise<AcquireFenceIfResult>;
89
+ /**
90
+ * SPI 2.2.0 (ADR 0116 amendment) — one synchronous step after the tick, so the existence,
91
+ * head and token checks and the commit cannot interleave with any other append, acquire or
92
+ * delete. The token is compared, never presented: the fence rule is deliberately skipped.
93
+ */
94
+ appendIfUnchanged(executionId: string, event: EventEnvelope | null, expect?: TipObservation): Promise<AppendIfUnchangedResult>;
95
+ deleteExecution(executionId: string): Promise<void>;
96
+ deleteThrough(executionId: string, through: number): Promise<void>;
97
+ private setLog;
98
+ listExecutions(): Promise<string[]>;
99
+ listExecutionsPage(opts?: {
100
+ cursor?: string;
101
+ limit?: number;
102
+ }): Promise<{
103
+ executionIds: string[];
104
+ nextCursor?: string;
105
+ }>;
106
+ truncatePrefix(executionId: string, throughSequence: number): Promise<void>;
107
+ truncatePrefixKeeping(executionId: string, throughSequence: number, keepEventTypes: readonly string[]): Promise<void>;
108
+ subscribe(executionId: string, handler: (event: EventEnvelope) => void): Promise<() => void>;
109
+ close(): Promise<void>;
110
+ }
111
+ //# sourceMappingURL=InMemoryAsyncEventStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryAsyncEventStore.d.ts","sourceRoot":"","sources":["../src/InMemoryAsyncEventStore.ts"],"names":[],"mappings":"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"}