@loomwork/event-runtime 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +95 -2
- package/dist/BranchRegistry.d.ts +140 -0
- package/dist/BranchRegistry.d.ts.map +1 -0
- package/dist/BranchRegistry.js +66 -0
- package/dist/BranchRegistry.js.map +1 -0
- package/dist/CoalescingEventStore.d.ts +97 -0
- package/dist/CoalescingEventStore.d.ts.map +1 -0
- package/dist/CoalescingEventStore.js +180 -0
- package/dist/CoalescingEventStore.js.map +1 -0
- package/dist/InMemoryArchiveStore.d.ts +19 -0
- package/dist/InMemoryArchiveStore.d.ts.map +1 -0
- package/dist/InMemoryArchiveStore.js +49 -0
- package/dist/InMemoryArchiveStore.js.map +1 -0
- package/dist/InMemoryAsyncEventStore.d.ts +111 -0
- package/dist/InMemoryAsyncEventStore.d.ts.map +1 -0
- package/dist/InMemoryAsyncEventStore.js +374 -0
- package/dist/InMemoryAsyncEventStore.js.map +1 -0
- package/dist/InMemoryBranchRegistry.d.ts +31 -0
- package/dist/InMemoryBranchRegistry.d.ts.map +1 -0
- package/dist/InMemoryBranchRegistry.js +167 -0
- package/dist/InMemoryBranchRegistry.js.map +1 -0
- package/dist/ProfilingEventStore.d.ts +75 -0
- package/dist/ProfilingEventStore.d.ts.map +1 -0
- package/dist/ProfilingEventStore.js +133 -0
- package/dist/ProfilingEventStore.js.map +1 -0
- package/dist/ShardPlan.d.ts +87 -0
- package/dist/ShardPlan.d.ts.map +1 -0
- package/dist/ShardPlan.js +193 -0
- package/dist/ShardPlan.js.map +1 -0
- package/dist/ShardedEventStore.d.ts +149 -0
- package/dist/ShardedEventStore.d.ts.map +1 -0
- package/dist/ShardedEventStore.js +468 -0
- package/dist/ShardedEventStore.js.map +1 -0
- package/dist/SubscriberRegistry.d.ts +59 -0
- package/dist/SubscriberRegistry.d.ts.map +1 -0
- package/dist/SubscriberRegistry.js +113 -0
- package/dist/SubscriberRegistry.js.map +1 -0
- package/dist/TenantScopedArchiveStore.d.ts +36 -0
- package/dist/TenantScopedArchiveStore.d.ts.map +1 -0
- package/dist/TenantScopedArchiveStore.js +96 -0
- package/dist/TenantScopedArchiveStore.js.map +1 -0
- package/dist/TenantScopedEventStore.d.ts +59 -0
- package/dist/TenantScopedEventStore.d.ts.map +1 -0
- package/dist/TenantScopedEventStore.js +227 -0
- package/dist/TenantScopedEventStore.js.map +1 -0
- package/dist/archive.d.ts +68 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +207 -0
- package/dist/archive.js.map +1 -0
- package/dist/archiveSpi.d.ts +165 -0
- package/dist/archiveSpi.d.ts.map +1 -0
- package/dist/archiveSpi.js +184 -0
- package/dist/archiveSpi.js.map +1 -0
- package/dist/creationMarker.d.ts +13 -0
- package/dist/creationMarker.d.ts.map +1 -0
- package/dist/creationMarker.js +17 -0
- package/dist/creationMarker.js.map +1 -0
- package/dist/decoratorPlan.typecheck.d.ts +2 -0
- package/dist/decoratorPlan.typecheck.d.ts.map +1 -0
- package/dist/decoratorPlan.typecheck.js +41 -0
- package/dist/decoratorPlan.typecheck.js.map +1 -0
- package/dist/eventKindClass.d.ts +40 -0
- package/dist/eventKindClass.d.ts.map +1 -0
- package/dist/eventKindClass.js +99 -0
- package/dist/eventKindClass.js.map +1 -0
- package/dist/executionIdOrder.d.ts +21 -0
- package/dist/executionIdOrder.d.ts.map +1 -0
- package/dist/executionIdOrder.js +43 -0
- package/dist/executionIdOrder.js.map +1 -0
- package/dist/fencing.d.ts +21 -0
- package/dist/fencing.d.ts.map +1 -0
- package/dist/fencing.js +36 -0
- package/dist/fencing.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/installOptional.d.ts +52 -0
- package/dist/installOptional.d.ts.map +1 -0
- package/dist/installOptional.js +112 -0
- package/dist/installOptional.js.map +1 -0
- package/dist/keyUnavailable.d.ts +67 -0
- package/dist/keyUnavailable.d.ts.map +1 -0
- package/dist/keyUnavailable.js +87 -0
- package/dist/keyUnavailable.js.map +1 -0
- package/dist/leases.d.ts +177 -0
- package/dist/leases.d.ts.map +1 -0
- package/dist/leases.js +142 -0
- package/dist/leases.js.map +1 -0
- package/dist/payloadShredded.d.ts +30 -0
- package/dist/payloadShredded.d.ts.map +1 -0
- package/dist/payloadShredded.js +67 -0
- package/dist/payloadShredded.js.map +1 -0
- package/dist/scaleDecorators.bench.d.ts +2 -0
- package/dist/scaleDecorators.bench.d.ts.map +1 -0
- package/dist/scaleDecorators.bench.js +93 -0
- package/dist/scaleDecorators.bench.js.map +1 -0
- package/dist/sequenceRange.bench.d.ts +2 -0
- package/dist/sequenceRange.bench.d.ts.map +1 -0
- package/dist/sequenceRange.bench.js +46 -0
- package/dist/sequenceRange.bench.js.map +1 -0
- package/dist/sequenceRange.d.ts +10 -0
- package/dist/sequenceRange.d.ts.map +1 -0
- package/dist/sequenceRange.js +35 -0
- package/dist/sequenceRange.js.map +1 -0
- package/dist/timers.d.ts +87 -0
- package/dist/timers.d.ts.map +1 -0
- package/dist/timers.js +68 -0
- package/dist/timers.js.map +1 -0
- package/dist/types.d.ts +634 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +429 -0
- package/dist/types.js.map +1 -0
- package/dist/writeConflicts.d.ts +46 -0
- package/dist/writeConflicts.d.ts.map +1 -0
- package/dist/writeConflicts.js +63 -0
- package/dist/writeConflicts.js.map +1 -0
- package/memory.d.ts +4 -0
- package/memory.js +13 -0
- package/package.json +67 -3
- package/sequence.d.ts +1 -0
- package/sequence.js +5 -0
- package/src/BranchRegistry.ts +161 -0
- package/src/CoalescingEventStore.ts +214 -0
- package/src/InMemoryArchiveStore.ts +69 -0
- package/src/InMemoryAsyncEventStore.ts +425 -0
- package/src/InMemoryBranchRegistry.ts +178 -0
- package/src/ProfilingEventStore.ts +153 -0
- package/src/ShardPlan.ts +235 -0
- package/src/ShardedEventStore.ts +557 -0
- package/src/SubscriberRegistry.ts +115 -0
- package/src/TenantScopedArchiveStore.ts +114 -0
- package/src/TenantScopedEventStore.ts +266 -0
- package/src/archive.ts +248 -0
- package/src/archiveSpi.ts +308 -0
- package/src/creationMarker.ts +15 -0
- package/src/decoratorPlan.typecheck.ts +52 -0
- package/src/eventKindClass.ts +127 -0
- package/src/executionIdOrder.ts +38 -0
- package/src/fencing.ts +31 -0
- package/src/index.ts +151 -0
- package/src/installOptional.ts +136 -0
- package/src/keyUnavailable.ts +98 -0
- package/src/leases.ts +319 -0
- package/src/payloadShredded.ts +63 -0
- package/src/scaleDecorators.bench.ts +107 -0
- package/src/sequenceRange.bench.ts +51 -0
- package/src/sequenceRange.ts +35 -0
- package/src/timers.ts +150 -0
- package/src/types.ts +857 -0
- package/src/writeConflicts.ts +56 -0
package/dist/types.js
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OPTIONAL_EVENT_STORE_METHODS = exports.RESERVED_LATE_PREFIX = exports.INBOX_LAYOUT_STREAM = exports.INBOX_REGISTRY_SUFFIX = exports.INBOX_TOPIC_SEQ_BASE = exports.QUARANTINE_STREAM_PREFIX = exports.INBOX_STREAM_PREFIX = exports.RESERVED_STREAM_PREFIX = exports.EVENT_STORE_SPI_VERSION = void 0;
|
|
4
|
+
exports.isReservedStream = isReservedStream;
|
|
5
|
+
exports.quarantineStreamId = quarantineStreamId;
|
|
6
|
+
exports.chainIdOfFirst = chainIdOfFirst;
|
|
7
|
+
exports.reservedLateKey = reservedLateKey;
|
|
8
|
+
exports.reservedLateOrigin = reservedLateOrigin;
|
|
9
|
+
exports.inboxTopicHash = inboxTopicHash;
|
|
10
|
+
exports.inboxTopicStreamId = inboxTopicStreamId;
|
|
11
|
+
exports.inboxRegistryStreamId = inboxRegistryStreamId;
|
|
12
|
+
exports.hasInboxStreamShape = hasInboxStreamShape;
|
|
13
|
+
exports.inboxChainOf = inboxChainOf;
|
|
14
|
+
exports.archiveChainOf = archiveChainOf;
|
|
15
|
+
exports.inboxStreamsOfChain = inboxStreamsOfChain;
|
|
16
|
+
exports.inboxStreamsOfChainDeep = inboxStreamsOfChainDeep;
|
|
17
|
+
exports.iterateExecutionIds = iterateExecutionIds;
|
|
18
|
+
exports.appendBatchOf = appendBatchOf;
|
|
19
|
+
exports.headOf = headOf;
|
|
20
|
+
exports.readRangeOf = readRangeOf;
|
|
21
|
+
const node_crypto_1 = require("node:crypto");
|
|
22
|
+
/**
|
|
23
|
+
* SPI version for the {@link EventStore} contract (ADR 0033). Bump MINOR when adding an
|
|
24
|
+
* optional method, MAJOR when changing a required method's contract. Pinned by
|
|
25
|
+
* `runEventStoreConformance` in `@loomwork/testing`.
|
|
26
|
+
*
|
|
27
|
+
* 1.1.0: added optional `listExecutions()` (enumerate distinct executionIds — powers
|
|
28
|
+
* `loom retention run` / `loom compliance report` over cold storage).
|
|
29
|
+
* 1.2.0: added optional `listExecutionsPage()` (SP-1, db-copilot infra #184 — keyset-paged
|
|
30
|
+
* enumeration so maintenance passes run in bounded memory at year scale). Consumers use
|
|
31
|
+
* {@link iterateExecutionIds}, which prefers pages and falls back to `listExecutions()`.
|
|
32
|
+
* 1.3.0: added optional `appendBatch()` (ADR 0074 D1 — one round-trip for many envelopes,
|
|
33
|
+
* which is what lets `CoalescingEventStore` raise the write ceiling). Consumers use
|
|
34
|
+
* {@link appendBatchOf}, which prefers it and falls back to a per-envelope `append` loop,
|
|
35
|
+
* so a backend that omits it is byte-identical to before.
|
|
36
|
+
* 1.4.0: added optional `head()` (the tip as one indexed `MAX(sequence)` — `fork()` was
|
|
37
|
+
* reading the whole parent log to validate a fork point). Consumers use {@link headOf}.
|
|
38
|
+
* 1.5.0: added optional `truncatePrefixKeeping()` (ADR 0106 — compaction that keeps an app's
|
|
39
|
+
* pinned event kinds, ADR 0055 D1). A store that omits it never loses an event, but
|
|
40
|
+
* `LogLifecycle.compact` then can NEVER compact past the first pinned event (it stops just
|
|
41
|
+
* below it on every pass, and warns): a long-lived execution that records a finding early
|
|
42
|
+
* keeps an unbounded replay tail on such a store.
|
|
43
|
+
* 2.0.0: `listExecutionsPage()` ORDER and CURSOR pinned (L10, ADR 0108). "Lexicographic"
|
|
44
|
+
* named no one order: a Postgres store paged in the database collation (`en_US.UTF-8`:
|
|
45
|
+
* `a_b` < `A1`) while `ShardedEventStore` merged in JavaScript's (`A1` < `a_b`), and
|
|
46
|
+
* resuming every shard from the merged page's last id skipped executions. Ids now page in
|
|
47
|
+
* Unicode CODE POINT order ({@link compareExecutionIds}; = UTF-8 byte order, SQL
|
|
48
|
+
* `COLLATE "C"`, SQLite BINARY); the cursor, which was OPAQUE, is now an executionId — any
|
|
49
|
+
* string, the page holding the ids strictly after it; and `nextCursor` MUST be the page's
|
|
50
|
+
* last id. MAJOR because a conforming 1.x store (any order, any opaque cursor token) can
|
|
51
|
+
* fail the kit, and `TenantScopedEventStore`/`ShardedEventStore` now depend on all three —
|
|
52
|
+
* the same rule that made MemoryStore 2.0.0 (ADR 0091). No method was added or removed: a
|
|
53
|
+
* store that already paged ids in that order with id cursors (SQLite; the in-memory store
|
|
54
|
+
* on well-formed BMP ids) conforms unchanged.
|
|
55
|
+
* 2.1.0: the optional `acquireFenceIf(id, { fence, parkSeq })` (ADR 0118 R8k: an atomic
|
|
56
|
+
* compare-and-set on the fence token AND "no answering event past the park"). The constant
|
|
57
|
+
* moved with the rest of the Wave 2 bump (W2-0); the method, its conformance cases and the
|
|
58
|
+
* decorator forwarding landed with R8k. It reads the classification W2-0 shipped:
|
|
59
|
+
* {@link EVENT_KIND_CLASS} / {@link isAnsweringKind} (every kernel event kind is `answering`
|
|
60
|
+
* or `ignorable`, a required total map). No existing method changed, so a 2.0.0 store conforms.
|
|
61
|
+
* 2.2.0: the optional `appendIfUnchanged(id, event, { head, fence, anchor })` (ADR 0116 amendment, R11:
|
|
62
|
+
* a late reward on a finished execution). ONE atomic step: append `event` iff the execution
|
|
63
|
+
* still EXISTS (sequence 0), its stored head is exactly `expect.head` AND its fence token is
|
|
64
|
+
* exactly `expect.fence`; otherwise change nothing and report the current tip. It never
|
|
65
|
+
* presents or adopts a fence token, so it can neither supersede a live holder nor be
|
|
66
|
+
* written under one it did not observe. A store that omits it makes `recordOutcome` refuse.
|
|
67
|
+
* No existing method changed, so a 2.1.0 store conforms.
|
|
68
|
+
* 2.3.0: a SEMANTIC minor, no signature change (ADR 0129). The `anchor` of an observation MUST
|
|
69
|
+
* differ between two incarnations of an execution id, EVEN WHEN their sequence-0 envelopes
|
|
70
|
+
* are byte-identical (a delete followed by a re-creation under a frozen clock). A store
|
|
71
|
+
* claiming 2.3.0 therefore assigns a monotonic incarnation when it durably creates sequence 0
|
|
72
|
+
* and returns it as the anchor; it lives in STORE METADATA only, never in an event, a fold, a
|
|
73
|
+
* snapshot or a hash, so replay stays byte-identical. A log created before the store gained
|
|
74
|
+
* that metadata may keep the legacy {@link creationMarker} (`timestamp:id`) until it is
|
|
75
|
+
* deleted (no backfill). A store that cannot assign one keeps 2.2.0 behaviour and does not
|
|
76
|
+
* claim 2.3.0.
|
|
77
|
+
* 2.4.0: the optional `storeNow(): Promise<number>` (ADR 0110 amendment, D10a) - the store's OWN
|
|
78
|
+
* clock in epoch ms, the one a multi-node fleet shares (Postgres: database time), the EventStore
|
|
79
|
+
* twin of `LeaseStore.storeNow()` (L29). It lets two nodes agree on an age (a message's TTL is
|
|
80
|
+
* judged against the instant it was stamped) without trusting either node's wall clock. A
|
|
81
|
+
* single-process store (memory, SQLite) need not implement it; a caller then falls back to its
|
|
82
|
+
* injected clock. Values must be finite, non-negative, and non-decreasing across sequential
|
|
83
|
+
* calls. Additive: no existing method changed, so a 2.3.0 store conforms.
|
|
84
|
+
* 2.5.0: the optional `sealingCapability(): SealingCapability | undefined` (ADR 0130 residuals D7,
|
|
85
|
+
* RESERVED - no behaviour yet): what this node's store seals AND therefore opens, so the kernel
|
|
86
|
+
* can compare it to a `metadata.seal` marker ({@link SealMarker}) on a stored envelope. Absent, or
|
|
87
|
+
* returning `undefined`, means "seals nothing, opens nothing" (the correct reading for a store
|
|
88
|
+
* that cannot open sealed data). Every decorator forwards, overrides or refuses it (it is listed
|
|
89
|
+
* in {@link OPTIONAL_EVENT_STORE_METHODS}); `ShardedEventStore` returns a capability only when
|
|
90
|
+
* every shard agrees. Also reserves the control stream prefix {@link QUARANTINE_STREAM_PREFIX}
|
|
91
|
+
* (`__loom_quarantine__:<chainId>`). Also the optional `readAtRest(): AtRestEventReader` (ADR 0130
|
|
92
|
+
* D9): a read-only view of the envelopes AS STORED (never decrypted), so the quarantine verbs can
|
|
93
|
+
* step over a sealed value without asking the KeyStore; replaces a hidden symbol protocol. Every
|
|
94
|
+
* decorator forwards it (`EncryptingEventStore` overrides it with its inner store's raw reads,
|
|
95
|
+
* `ShardedEventStore` routes per stream). Additive: no existing method changed, so a 2.4.0 store
|
|
96
|
+
* conforms.
|
|
97
|
+
* 2.6.0: the optional `deleteThrough(executionId, through)` (ADR 0139 D8): one atomic delete of every
|
|
98
|
+
* event with `sequence <= through`, sequence 0 included. A caller that archived a stream up to
|
|
99
|
+
* `through` (the terminal delete of a durable inbox) removes exactly what it archived, so a message
|
|
100
|
+
* appended in between survives. Every decorator forwards it. Additive: no existing method changed, so a
|
|
101
|
+
* 2.5.0 store conforms (a caller then refuses the delete it cannot make safe).
|
|
102
|
+
*
|
|
103
|
+
* Every optional method is listed in {@link OPTIONAL_EVENT_STORE_METHODS}; adding one here
|
|
104
|
+
* without listing it there is a compile error. `OptionalEventStoreMethod` is derived from this
|
|
105
|
+
* interface, and `DecoratorPlan` makes every one of its keys REQUIRED, so a new optional method
|
|
106
|
+
* is also a compile error in every decorator whose plan `satisfies DecoratorPlan<EventStore,
|
|
107
|
+
* OptionalEventStoreMethod>` until it forwards, overrides or refuses it. A decorator that uses
|
|
108
|
+
* no plan at all is caught by the decorator conformance kit instead (and the `packages/spec`
|
|
109
|
+
* fitness test makes every in-repo decorator run it).
|
|
110
|
+
*/
|
|
111
|
+
exports.EVENT_STORE_SPI_VERSION = "2.6.0";
|
|
112
|
+
/**
|
|
113
|
+
* Reserved-stream prefix. Control facts (retention `__loom_lifecycle__`, erasure
|
|
114
|
+
* `__loom_erasure__`) are stored under executionIds beginning with this, so
|
|
115
|
+
* {@link listExecutions} surfaces them too. Enumerators over *real* executions filter with
|
|
116
|
+
* {@link isReservedStream}.
|
|
117
|
+
*/
|
|
118
|
+
exports.RESERVED_STREAM_PREFIX = "__loom_";
|
|
119
|
+
/**
|
|
120
|
+
* Whether an executionId names a reserved control stream rather than a real execution.
|
|
121
|
+
*
|
|
122
|
+
* The blank id `""` is reserved too (ADR 0107). It names no execution, and it is where the
|
|
123
|
+
* pre-ADR-0106 compaction bug wrote the events of every resume that followed a compaction.
|
|
124
|
+
* Reserving it keeps enumerators from treating that stream as an execution to recover, retain
|
|
125
|
+
* or report, and keeps every runtime entry point from writing to it again.
|
|
126
|
+
*/
|
|
127
|
+
function isReservedStream(executionId) {
|
|
128
|
+
return executionId === "" || executionId.startsWith(exports.RESERVED_STREAM_PREFIX);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The reserved control stream a chain's durable inbox lives in (ADR 0110 D1):
|
|
132
|
+
* `__loom_inbox__:<chainId>`. Defined here, beside {@link isReservedStream}, so a store decorator
|
|
133
|
+
* (the crypto plugin's `EncryptingEventStore`) and the compliance erasure can recognise it without
|
|
134
|
+
* depending on the workflow runtime.
|
|
135
|
+
*/
|
|
136
|
+
exports.INBOX_STREAM_PREFIX = "__loom_inbox__:";
|
|
137
|
+
/**
|
|
138
|
+
* (SPI 2.5.0, ADR 0130 residuals D9; written by `quarantineInbox`): the control stream holding the
|
|
139
|
+
* verbatim copies of a chain's QUARANTINED inbox messages, `__loom_quarantine__:<chainId>`. It
|
|
140
|
+
* belongs to the same chain as the inbox, so {@link inboxChainOf} maps it to the chain (the crypto
|
|
141
|
+
* subject mapping, purge and delete-with-chain then cover it) and {@link InboxStreams.quarantine}
|
|
142
|
+
* names it. Reserved by prefix like every `__loom_` stream.
|
|
143
|
+
*/
|
|
144
|
+
exports.QUARANTINE_STREAM_PREFIX = "__loom_quarantine__:";
|
|
145
|
+
/** The reserved stream holding `chainId`'s quarantined messages (see {@link QUARANTINE_STREAM_PREFIX}). */
|
|
146
|
+
function quarantineStreamId(chainId) {
|
|
147
|
+
return `${exports.QUARANTINE_STREAM_PREFIX}${chainId}`;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The chain an execution belongs to, read from its sequence 0 alone (the fold's own rule: a valid `forkChain` stamp wins,
|
|
151
|
+
* else the start payload's `chainId`, else the execution itself) — so `send` needs no fold.
|
|
152
|
+
*/
|
|
153
|
+
function chainIdOfFirst(first, executionId) {
|
|
154
|
+
const valid = (v) => {
|
|
155
|
+
if (v === null || typeof v !== "object")
|
|
156
|
+
return undefined;
|
|
157
|
+
const { chainId, generation } = v;
|
|
158
|
+
return typeof chainId === "string" &&
|
|
159
|
+
chainId.length > 0 &&
|
|
160
|
+
Number.isSafeInteger(generation) &&
|
|
161
|
+
generation >= 0
|
|
162
|
+
? chainId
|
|
163
|
+
: undefined;
|
|
164
|
+
};
|
|
165
|
+
return valid(first.metadata?.forkChain) ?? valid(first.payload) ?? executionId;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* ADR 0110 amendment, D10b — per-topic inbox streams. A chain's inbox may hold one stream per topic,
|
|
169
|
+
* beside the legacy single stream:
|
|
170
|
+
*
|
|
171
|
+
* - `__loom_inbox__:<chainId>#<16 hex>` — the messages of ONE topic (the first 16 hex digits of the
|
|
172
|
+
* SHA-256 of the topic name);
|
|
173
|
+
* - `__loom_inbox__:<chainId>#reg` — the chain's REGISTRY: one `INBOX_TOPIC` event per topic that
|
|
174
|
+
* ever had a stream, so every stream of a chain is enumerable without `listExecutionsPage`.
|
|
175
|
+
*
|
|
176
|
+
* The cursors stay ONE number space (the fold is unchanged): a topic stream's sequences start at
|
|
177
|
+
* {@link INBOX_TOPIC_SEQ_BASE}, far above any legacy-stream sequence, so a legacy message of a topic
|
|
178
|
+
* (below the base) always precedes its topic-stream messages and the two can never collide.
|
|
179
|
+
*/
|
|
180
|
+
exports.INBOX_TOPIC_SEQ_BASE = 2 ** 40;
|
|
181
|
+
/** The suffix of a chain's registry stream (see {@link INBOX_TOPIC_SEQ_BASE}). */
|
|
182
|
+
exports.INBOX_REGISTRY_SUFFIX = "#reg";
|
|
183
|
+
/**
|
|
184
|
+
* The control stream whose existence is the FLEET-WIDE opt-in to per-topic writing (ADR 0110 D10b):
|
|
185
|
+
* one `INBOX_LAYOUT` event, written once by `runtime.enableInboxTopicStreams`. Reserved by prefix.
|
|
186
|
+
*/
|
|
187
|
+
exports.INBOX_LAYOUT_STREAM = "__loom_inbox_layout__";
|
|
188
|
+
const TOPIC_STREAM_SUFFIX = /#[0-9a-f]{16}$/;
|
|
189
|
+
/**
|
|
190
|
+
* ADR 0139 D4: the prefix of an ARCHIVE key (never a live stream) that holds envelopes of a reserved
|
|
191
|
+
* stream which could not extend the stream's main archive run: a straggler appended at or below the
|
|
192
|
+
* archive's last sequence, for example by a send that raced the owner's terminal delete. The key is
|
|
193
|
+
* `__loom_late__:<stream>@<n>`. It begins with {@link RESERVED_STREAM_PREFIX}, which `start` refuses for
|
|
194
|
+
* every execution id, so no live chain or execution can ever have that shape, and {@link inboxChainOf}
|
|
195
|
+
* (live ids only) is untouched: an archive-key context asks {@link archiveChainOf} instead.
|
|
196
|
+
*/
|
|
197
|
+
exports.RESERVED_LATE_PREFIX = "__loom_late__:";
|
|
198
|
+
const LATE_KEY = /^(.+)@([1-9][0-9]*)$/;
|
|
199
|
+
/** The archive key of the `n`th (1-based) late run of a reserved stream (see {@link RESERVED_LATE_PREFIX}). */
|
|
200
|
+
function reservedLateKey(streamId, n) {
|
|
201
|
+
return `${exports.RESERVED_LATE_PREFIX}${streamId}@${n}`;
|
|
202
|
+
}
|
|
203
|
+
/** The reserved stream (and run number) an archive key of {@link reservedLateKey} shape belongs to. */
|
|
204
|
+
function reservedLateOrigin(key) {
|
|
205
|
+
if (!key.startsWith(exports.RESERVED_LATE_PREFIX))
|
|
206
|
+
return undefined;
|
|
207
|
+
const m = LATE_KEY.exec(key.slice(exports.RESERVED_LATE_PREFIX.length));
|
|
208
|
+
return m === null ? undefined : { streamId: m[1], n: Number(m[2]) };
|
|
209
|
+
}
|
|
210
|
+
/** The 16-hex-digit stream suffix of `topic` (first 8 bytes of its SHA-256). */
|
|
211
|
+
function inboxTopicHash(topic) {
|
|
212
|
+
return (0, node_crypto_1.createHash)("sha256").update(topic).digest("hex").slice(0, 16);
|
|
213
|
+
}
|
|
214
|
+
/** The reserved stream holding `topic`'s messages for `chainId` (see {@link INBOX_TOPIC_SEQ_BASE}). */
|
|
215
|
+
function inboxTopicStreamId(chainId, topic) {
|
|
216
|
+
return `${exports.INBOX_STREAM_PREFIX}${chainId}#${inboxTopicHash(topic)}`;
|
|
217
|
+
}
|
|
218
|
+
/** The chain's registry stream (see {@link INBOX_TOPIC_SEQ_BASE}). */
|
|
219
|
+
function inboxRegistryStreamId(chainId) {
|
|
220
|
+
return `${exports.INBOX_STREAM_PREFIX}${chainId}${exports.INBOX_REGISTRY_SUFFIX}`;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Would an id, taken as a CHAIN id, alias a topic or registry stream of another chain
|
|
224
|
+
* (`<x>#<16 hex>` or `<x>#reg`)? Such an id is refused at `start` once the inbox is on.
|
|
225
|
+
*/
|
|
226
|
+
function hasInboxStreamShape(id) {
|
|
227
|
+
return id.endsWith(exports.INBOX_REGISTRY_SUFFIX) || TOPIC_STREAM_SUFFIX.test(id);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The chain id an inbox stream belongs to, or `undefined` when `executionId` is not an inbox
|
|
231
|
+
* stream (or names an empty chain). A chain's id is its generation-0 execution id (or a fork's
|
|
232
|
+
* fresh one), so a decorator that maps executions to erasure subjects can map the chain's streams
|
|
233
|
+
* (legacy, per-topic and registry alike) to the same subject as its owner.
|
|
234
|
+
*/
|
|
235
|
+
function inboxChainOf(executionId) {
|
|
236
|
+
if (executionId.startsWith(exports.QUARANTINE_STREAM_PREFIX)) {
|
|
237
|
+
// ADR 0130 residuals D9 (reserved): the quarantine copy belongs to the chain, under the same owner subject.
|
|
238
|
+
const quarantined = executionId.slice(exports.QUARANTINE_STREAM_PREFIX.length);
|
|
239
|
+
return quarantined.length > 0 ? quarantined : undefined;
|
|
240
|
+
}
|
|
241
|
+
if (!executionId.startsWith(exports.INBOX_STREAM_PREFIX))
|
|
242
|
+
return undefined;
|
|
243
|
+
let chain = executionId.slice(exports.INBOX_STREAM_PREFIX.length);
|
|
244
|
+
if (chain.endsWith(exports.INBOX_REGISTRY_SUFFIX))
|
|
245
|
+
chain = chain.slice(0, -exports.INBOX_REGISTRY_SUFFIX.length);
|
|
246
|
+
else if (TOPIC_STREAM_SUFFIX.test(chain))
|
|
247
|
+
chain = chain.slice(0, -17);
|
|
248
|
+
return chain.length > 0 ? chain : undefined;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* {@link inboxChainOf} for an ARCHIVE key: the same chain for a reserved stream's own key, and, for a
|
|
252
|
+
* late-run key ({@link reservedLateKey}), the chain of the stream it was split from. Archive-key
|
|
253
|
+
* contexts only (the sealing subject of `EncryptingArchiveStore`, the archive listings); a live stream
|
|
254
|
+
* id is never a late key, so live code keeps calling {@link inboxChainOf}.
|
|
255
|
+
*/
|
|
256
|
+
function archiveChainOf(key) {
|
|
257
|
+
return inboxChainOf(reservedLateOrigin(key)?.streamId ?? key);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Every inbox stream a chain has on `store`: the legacy stream, the registry (when it exists) and one
|
|
261
|
+
* stream per registered topic. The registry is bounded by the per-chain topic cap, so this is one small
|
|
262
|
+
* read. Used by every site that must reach ALL of a chain's streams (retirement, erasure, lifecycle
|
|
263
|
+
* deletion) without a second copy of the naming rule. Delete `topics`, then `legacy`, then `registry`
|
|
264
|
+
* LAST: a crash midway then leaves the remaining topic streams still findable.
|
|
265
|
+
*/
|
|
266
|
+
async function inboxStreamsOfChain(store, chainId) {
|
|
267
|
+
const registry = inboxRegistryStreamId(chainId);
|
|
268
|
+
const entries = await store.read(registry);
|
|
269
|
+
const topics = [];
|
|
270
|
+
const seen = new Set();
|
|
271
|
+
for (const e of entries) {
|
|
272
|
+
const topic = e.payload?.topic;
|
|
273
|
+
if (typeof topic !== "string" || seen.has(topic))
|
|
274
|
+
continue;
|
|
275
|
+
seen.add(topic);
|
|
276
|
+
topics.push(inboxTopicStreamId(chainId, topic));
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
legacy: `${exports.INBOX_STREAM_PREFIX}${chainId}`,
|
|
280
|
+
registry: entries.length > 0 ? registry : undefined,
|
|
281
|
+
topics,
|
|
282
|
+
quarantine: quarantineStreamId(chainId),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* {@link inboxStreamsOfChain} PLUS a prefix scan: the topic streams the registry does not name (a
|
|
287
|
+
* sender that crashed between its append and its registration, a registry deleted underneath a
|
|
288
|
+
* sender, a stale "already registered" cache). Reaches them through the store's keyset-paged
|
|
289
|
+
* enumeration when it has one (`listExecutionsPage`, ids in code point order, so the chain's streams are
|
|
290
|
+
* one contiguous run after its prefix); a store without it gets the registry's answer alone. It is a
|
|
291
|
+
* BACKSTOP for erasure and the orphan sweep, not the hot path (the scan is one indexed range query).
|
|
292
|
+
*/
|
|
293
|
+
async function inboxStreamsOfChainDeep(store, chainId) {
|
|
294
|
+
const found = await inboxStreamsOfChain(store, chainId);
|
|
295
|
+
if (store.listExecutionsPage === undefined)
|
|
296
|
+
return found;
|
|
297
|
+
const prefix = `${exports.INBOX_STREAM_PREFIX}${chainId}#`;
|
|
298
|
+
const registry = inboxRegistryStreamId(chainId);
|
|
299
|
+
const known = new Set(found.topics);
|
|
300
|
+
let hasRegistry = found.registry !== undefined;
|
|
301
|
+
let cursor = prefix;
|
|
302
|
+
for (;;) {
|
|
303
|
+
const page = await store.listExecutionsPage({ cursor, limit: 200 });
|
|
304
|
+
let past = false;
|
|
305
|
+
for (const id of page.executionIds) {
|
|
306
|
+
if (!id.startsWith(prefix)) {
|
|
307
|
+
past = true;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
if (id === registry)
|
|
311
|
+
hasRegistry = true;
|
|
312
|
+
else if (TOPIC_STREAM_SUFFIX.test(id) && id.length === prefix.length + 16 && !known.has(id)) {
|
|
313
|
+
known.add(id);
|
|
314
|
+
found.topics.push(id);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (past || page.nextCursor === undefined || page.nextCursor === cursor)
|
|
318
|
+
break;
|
|
319
|
+
cursor = page.nextCursor;
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
legacy: found.legacy,
|
|
323
|
+
registry: hasRegistry ? registry : undefined,
|
|
324
|
+
topics: found.topics,
|
|
325
|
+
quarantine: found.quarantine,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The runtime list of {@link OptionalEventStoreMethod}, for the decorator conformance kit.
|
|
330
|
+
* The check below makes a new optional method on the interface a compile error here until it
|
|
331
|
+
* is listed — so the kit can never quietly stop checking one.
|
|
332
|
+
*/
|
|
333
|
+
exports.OPTIONAL_EVENT_STORE_METHODS = [
|
|
334
|
+
"appendBatch",
|
|
335
|
+
"readRange",
|
|
336
|
+
"head",
|
|
337
|
+
"acquireFence",
|
|
338
|
+
"acquireFenceIf",
|
|
339
|
+
"appendIfUnchanged",
|
|
340
|
+
"storeNow",
|
|
341
|
+
"sealingCapability",
|
|
342
|
+
"readAtRest",
|
|
343
|
+
"copyPrefix",
|
|
344
|
+
"deleteExecution",
|
|
345
|
+
"deleteThrough",
|
|
346
|
+
"truncatePrefix",
|
|
347
|
+
"truncatePrefixKeeping",
|
|
348
|
+
"listExecutions",
|
|
349
|
+
"listExecutionsPage",
|
|
350
|
+
];
|
|
351
|
+
// A new optional EventStore method that is not listed above makes this line a compile error.
|
|
352
|
+
const _exhaustiveOptionalMethods = true;
|
|
353
|
+
void _exhaustiveOptionalMethods;
|
|
354
|
+
/**
|
|
355
|
+
* SP-2 — the one enumeration loop every maintenance pass shares: prefers the
|
|
356
|
+
* bounded-memory paged surface, falls back to `listExecutions()`, and REFUSES a
|
|
357
|
+
* store with neither (silent partial passes are how retention deletes evidence
|
|
358
|
+
* and read models under-index).
|
|
359
|
+
*/
|
|
360
|
+
async function* iterateExecutionIds(source, pageSize = 1000) {
|
|
361
|
+
if (source.listExecutionsPage !== undefined) {
|
|
362
|
+
let cursor;
|
|
363
|
+
for (;;) {
|
|
364
|
+
const page = await source.listExecutionsPage({
|
|
365
|
+
...(cursor !== undefined && { cursor }),
|
|
366
|
+
limit: pageSize,
|
|
367
|
+
});
|
|
368
|
+
for (const id of page.executionIds)
|
|
369
|
+
yield id;
|
|
370
|
+
if (page.nextCursor === undefined)
|
|
371
|
+
return;
|
|
372
|
+
if (page.nextCursor === cursor) {
|
|
373
|
+
// Audit hardening: a buggy store returning a non-advancing cursor would
|
|
374
|
+
// spin every maintenance pass forever — refuse loudly instead.
|
|
375
|
+
throw new TypeError("iterateExecutionIds: listExecutionsPage returned a non-advancing cursor (store bug) — aborting the scan");
|
|
376
|
+
}
|
|
377
|
+
cursor = page.nextCursor;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (source.listExecutions !== undefined) {
|
|
381
|
+
for (const id of await source.listExecutions())
|
|
382
|
+
yield id;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
throw new TypeError("iterateExecutionIds: the store supports neither listExecutionsPage() nor listExecutions() — it cannot be enumerated");
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Append many envelopes through a store that may or may not implement the optional
|
|
389
|
+
* `appendBatch` fast-path (ADR 0074 D1). Uses the single round-trip when available, else
|
|
390
|
+
* falls back to a sequential `append` loop — so callers never branch on the capability.
|
|
391
|
+
*
|
|
392
|
+
* The fallback is deliberately **sequential**, not `Promise.all`: `append` is
|
|
393
|
+
* order-sensitive per execution, and a parallel loop would also lose the all-or-nothing
|
|
394
|
+
* property that the native path guarantees. The fallback is a correctness-preserving
|
|
395
|
+
* degradation, not a performance one — it wins nothing, and is not meant to.
|
|
396
|
+
*/
|
|
397
|
+
async function appendBatchOf(store, envelopes) {
|
|
398
|
+
if (envelopes.length === 0)
|
|
399
|
+
return;
|
|
400
|
+
if (store.appendBatch !== undefined)
|
|
401
|
+
return store.appendBatch(envelopes);
|
|
402
|
+
for (const envelope of envelopes)
|
|
403
|
+
await store.append(envelope);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Read `[fromSequence..toSequence]` from a store that may or may not implement the
|
|
407
|
+
* optional `readRange` fast-path. Uses the native indexed range when available,
|
|
408
|
+
* else falls back to `readFrom` + a bounded slice — so every caller gets the
|
|
409
|
+
* range without having to branch on the capability.
|
|
410
|
+
*/
|
|
411
|
+
/**
|
|
412
|
+
* The tip of an execution's log through the optional {@link EventStore.head} fast-path,
|
|
413
|
+
* falling back to reading it. `null` means the execution has no events at all.
|
|
414
|
+
*/
|
|
415
|
+
async function headOf(store, executionId) {
|
|
416
|
+
if (store.head)
|
|
417
|
+
return store.head(executionId);
|
|
418
|
+
const events = await store.read(executionId);
|
|
419
|
+
return events.length === 0 ? null : (events[events.length - 1]?.sequence ?? null);
|
|
420
|
+
}
|
|
421
|
+
async function readRangeOf(store, executionId, fromSequence, toSequence) {
|
|
422
|
+
if (toSequence < fromSequence)
|
|
423
|
+
return [];
|
|
424
|
+
if (store.readRange)
|
|
425
|
+
return store.readRange(executionId, fromSequence, toSequence);
|
|
426
|
+
const from = await store.readFrom(executionId, fromSequence);
|
|
427
|
+
return from.filter((e) => e.sequence <= toSequence);
|
|
428
|
+
}
|
|
429
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A write lost the race for a `(executionId, sequence)` slot: something else already
|
|
3
|
+
* occupies it. Every backend rejects this — SQLite and Postgres on their
|
|
4
|
+
* `UNIQUE (execution_id, sequence)` constraint, the reference in-memory store on an
|
|
5
|
+
* explicit check — and they normalize the failure to THIS type so a caller can tell a
|
|
6
|
+
* sequence conflict from an ordinary write error without matching on driver text.
|
|
7
|
+
*
|
|
8
|
+
* The sibling of {@link StaleFenceError}, and the case fencing does not cover. Fencing
|
|
9
|
+
* rejects a writer known to be superseded — it engages only once a token has been
|
|
10
|
+
* acquired for the execution. Sequences are allocated per-process from folded state
|
|
11
|
+
* (`ExecutionJournal` seeds from `vm.getState().sequence` and increments locally), so two
|
|
12
|
+
* unfenced writers recovering the same execution allocate the SAME next sequence and
|
|
13
|
+
* neither is a zombie. This is what stops the second one silently interleaving into the
|
|
14
|
+
* log.
|
|
15
|
+
*/
|
|
16
|
+
export declare class DuplicateSequenceError extends Error {
|
|
17
|
+
readonly executionId: string;
|
|
18
|
+
readonly sequence: number;
|
|
19
|
+
/** The driver error this was normalized from, when there was one. */
|
|
20
|
+
readonly cause?: unknown | undefined;
|
|
21
|
+
constructor(executionId: string, sequence: number,
|
|
22
|
+
/** The driver error this was normalized from, when there was one. */
|
|
23
|
+
cause?: unknown | undefined);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* An envelope re-used an `id` that is already in the log. Ids are unique **across the
|
|
27
|
+
* whole store**, not per execution — both SQL backends key `id` as the events table's
|
|
28
|
+
* PRIMARY KEY — and every backend rejects a collision rather than storing the second
|
|
29
|
+
* envelope or silently treating it as a re-delivery of the first.
|
|
30
|
+
*
|
|
31
|
+
* The other of the events table's two keys, and a genuinely different failure from
|
|
32
|
+
* {@link DuplicateSequenceError}: a taken SEQUENCE means another writer reached that slot
|
|
33
|
+
* in this execution's log (a lost race), while a taken ID means the same envelope identity
|
|
34
|
+
* was minted twice (a bug in whatever produced it). Reporting either as the other sends
|
|
35
|
+
* the reader hunting the wrong problem, which is why they are separate types.
|
|
36
|
+
*/
|
|
37
|
+
export declare class DuplicateEnvelopeIdError extends Error {
|
|
38
|
+
readonly envelopeId: string;
|
|
39
|
+
readonly executionId: string;
|
|
40
|
+
/** The driver error this was normalized from, when there was one. */
|
|
41
|
+
readonly cause?: unknown | undefined;
|
|
42
|
+
constructor(envelopeId: string, executionId: string,
|
|
43
|
+
/** The driver error this was normalized from, when there was one. */
|
|
44
|
+
cause?: unknown | undefined);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=writeConflicts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeConflicts.d.ts","sourceRoot":"","sources":["../src/writeConflicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;gBAHf,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM;IACzB,qEAAqE;IAC5D,KAAK,CAAC,EAAE,OAAO,YAAA;CAQ3B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAE/C,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;gBAHf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM;IAC5B,qEAAqE;IAC5D,KAAK,CAAC,EAAE,OAAO,YAAA;CAQ3B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DuplicateEnvelopeIdError = exports.DuplicateSequenceError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A write lost the race for a `(executionId, sequence)` slot: something else already
|
|
6
|
+
* occupies it. Every backend rejects this — SQLite and Postgres on their
|
|
7
|
+
* `UNIQUE (execution_id, sequence)` constraint, the reference in-memory store on an
|
|
8
|
+
* explicit check — and they normalize the failure to THIS type so a caller can tell a
|
|
9
|
+
* sequence conflict from an ordinary write error without matching on driver text.
|
|
10
|
+
*
|
|
11
|
+
* The sibling of {@link StaleFenceError}, and the case fencing does not cover. Fencing
|
|
12
|
+
* rejects a writer known to be superseded — it engages only once a token has been
|
|
13
|
+
* acquired for the execution. Sequences are allocated per-process from folded state
|
|
14
|
+
* (`ExecutionJournal` seeds from `vm.getState().sequence` and increments locally), so two
|
|
15
|
+
* unfenced writers recovering the same execution allocate the SAME next sequence and
|
|
16
|
+
* neither is a zombie. This is what stops the second one silently interleaving into the
|
|
17
|
+
* log.
|
|
18
|
+
*/
|
|
19
|
+
class DuplicateSequenceError extends Error {
|
|
20
|
+
executionId;
|
|
21
|
+
sequence;
|
|
22
|
+
cause;
|
|
23
|
+
constructor(executionId, sequence,
|
|
24
|
+
/** The driver error this was normalized from, when there was one. */
|
|
25
|
+
cause) {
|
|
26
|
+
super(`duplicate sequence ${sequence} for execution "${executionId}": that slot is already occupied (another writer reached it first)`);
|
|
27
|
+
this.executionId = executionId;
|
|
28
|
+
this.sequence = sequence;
|
|
29
|
+
this.cause = cause;
|
|
30
|
+
this.name = "DuplicateSequenceError";
|
|
31
|
+
Object.setPrototypeOf(this, DuplicateSequenceError.prototype);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.DuplicateSequenceError = DuplicateSequenceError;
|
|
35
|
+
/**
|
|
36
|
+
* An envelope re-used an `id` that is already in the log. Ids are unique **across the
|
|
37
|
+
* whole store**, not per execution — both SQL backends key `id` as the events table's
|
|
38
|
+
* PRIMARY KEY — and every backend rejects a collision rather than storing the second
|
|
39
|
+
* envelope or silently treating it as a re-delivery of the first.
|
|
40
|
+
*
|
|
41
|
+
* The other of the events table's two keys, and a genuinely different failure from
|
|
42
|
+
* {@link DuplicateSequenceError}: a taken SEQUENCE means another writer reached that slot
|
|
43
|
+
* in this execution's log (a lost race), while a taken ID means the same envelope identity
|
|
44
|
+
* was minted twice (a bug in whatever produced it). Reporting either as the other sends
|
|
45
|
+
* the reader hunting the wrong problem, which is why they are separate types.
|
|
46
|
+
*/
|
|
47
|
+
class DuplicateEnvelopeIdError extends Error {
|
|
48
|
+
envelopeId;
|
|
49
|
+
executionId;
|
|
50
|
+
cause;
|
|
51
|
+
constructor(envelopeId, executionId,
|
|
52
|
+
/** The driver error this was normalized from, when there was one. */
|
|
53
|
+
cause) {
|
|
54
|
+
super(`duplicate envelope id "${envelopeId}" (execution "${executionId}"): event ids are unique across the store, and that one is already taken`);
|
|
55
|
+
this.envelopeId = envelopeId;
|
|
56
|
+
this.executionId = executionId;
|
|
57
|
+
this.cause = cause;
|
|
58
|
+
this.name = "DuplicateEnvelopeIdError";
|
|
59
|
+
Object.setPrototypeOf(this, DuplicateEnvelopeIdError.prototype);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.DuplicateEnvelopeIdError = DuplicateEnvelopeIdError;
|
|
63
|
+
//# sourceMappingURL=writeConflicts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeConflicts.js","sourceRoot":"","sources":["../src/writeConflicts.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAa,sBAAuB,SAAQ,KAAK;IAEpC;IACA;IAEA;IAJX,YACW,WAAmB,EACnB,QAAgB;IACzB,qEAAqE;IAC5D,KAAe;QAExB,KAAK,CACH,sBAAsB,QAAQ,mBAAmB,WAAW,oEAAoE,CACjI,CAAC;QAPO,gBAAW,GAAX,WAAW,CAAQ;QACnB,aAAQ,GAAR,QAAQ,CAAQ;QAEhB,UAAK,GAAL,KAAK,CAAU;QAKxB,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;CACF;AAbD,wDAaC;AAED;;;;;;;;;;;GAWG;AACH,MAAa,wBAAyB,SAAQ,KAAK;IAEtC;IACA;IAEA;IAJX,YACW,UAAkB,EAClB,WAAmB;IAC5B,qEAAqE;IAC5D,KAAe;QAExB,KAAK,CACH,0BAA0B,UAAU,iBAAiB,WAAW,0EAA0E,CAC3I,CAAC;QAPO,eAAU,GAAV,UAAU,CAAQ;QAClB,gBAAW,GAAX,WAAW,CAAQ;QAEnB,UAAK,GAAL,KAAK,CAAU;QAKxB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAClE,CAAC;CACF;AAbD,4DAaC"}
|
package/memory.d.ts
ADDED
package/memory.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Browser-safe subpath: only the in-memory stores, so importing it never pulls
|
|
2
|
+
// the SQLite store (better-sqlite3 / node:fs) that the package barrel re-exports.
|
|
3
|
+
// (These packages compile to CommonJS, which bundlers cannot tree-shake — hence a
|
|
4
|
+
// dedicated subpath rather than relying on `sideEffects`.)
|
|
5
|
+
// The in-memory branch registry is node-free too; a browser that forks needs it.
|
|
6
|
+
module.exports = {
|
|
7
|
+
...require("./dist/InMemoryAsyncEventStore"),
|
|
8
|
+
...require("./dist/InMemoryBranchRegistry"),
|
|
9
|
+
// ADR 0138: the in-memory archive imports only the node-free SPI file (archiveSpi), not the digest helpers.
|
|
10
|
+
InMemoryArchiveStore: require("./dist/InMemoryArchiveStore").InMemoryArchiveStore,
|
|
11
|
+
// ADR 0130 D11: leases.ts has no imports, so the in-memory lease store is browser-safe too.
|
|
12
|
+
InMemoryLeaseStore: require("./dist/leases").InMemoryLeaseStore,
|
|
13
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loomwork/event-runtime",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Append-only event store for the Loom framework",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./memory": {
|
|
14
|
+
"types": "./memory.d.ts",
|
|
15
|
+
"require": "./memory.js",
|
|
16
|
+
"default": "./memory.js"
|
|
17
|
+
},
|
|
18
|
+
"./sequence": {
|
|
19
|
+
"types": "./sequence.d.ts",
|
|
20
|
+
"require": "./sequence.js",
|
|
21
|
+
"default": "./sequence.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@loomwork/core": "^0.1.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.0.0",
|
|
29
|
+
"typescript": "^5.6.3",
|
|
30
|
+
"vitest": "^2.1.8"
|
|
31
|
+
},
|
|
32
|
+
"license": "Apache-2.0",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/chhetrisushil/loom.git",
|
|
36
|
+
"directory": "packages/event-runtime"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"src",
|
|
41
|
+
"memory.d.ts",
|
|
42
|
+
"memory.js",
|
|
43
|
+
"sequence.d.ts",
|
|
44
|
+
"sequence.js",
|
|
45
|
+
"!src/**/*.test.ts",
|
|
46
|
+
"!src/**/*.test.tsx",
|
|
47
|
+
"!src/**/__tests__",
|
|
48
|
+
"!src/**/__fixtures__",
|
|
49
|
+
"!src/**/__snapshots__",
|
|
50
|
+
"!src/**/__golden__",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"NOTICE"
|
|
53
|
+
],
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"tag": "next"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=20.0.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsc --build",
|
|
63
|
+
"dev": "tsc --build --watch",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"test:watch": "vitest",
|
|
66
|
+
"bench": "vitest bench --run",
|
|
67
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
68
|
+
"typecheck:tests": "tsc -p tsconfig.test.json"
|
|
69
|
+
}
|
|
6
70
|
}
|