@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,59 @@
1
+ import type { EventEnvelope } from "@loomwork/core";
2
+ /**
3
+ * The backend-independent half of `EventStore.subscribe` (ADR 0074 D4).
4
+ *
5
+ * Every store has to do the same three things: keep a per-execution set of local handlers,
6
+ * fan an event out to them, and — for stores whose transport echoes an append back to its
7
+ * own publisher — ignore what it published itself. That was written three times
8
+ * (in-memory, SQLite, Postgres) before this existed.
9
+ *
10
+ * What it deliberately does NOT do is decide how events arrive. That is the genuinely
11
+ * backend-specific part: a global channel, a per-bucket channel, a Redis topic, a Kafka
12
+ * partition. {@link interest} exists so a transport can ask "what am I actually subscribed
13
+ * to?" and narrow what it asks for upstream instead of receiving everything and
14
+ * discarding — the difference between broadcast delivery and interest-filtered delivery.
15
+ *
16
+ * A bring-your-own backend composes this and implements only the transport.
17
+ */
18
+ export declare class SubscriberRegistry {
19
+ private readonly handlers;
20
+ /**
21
+ * Fires when an execution gains its first handler or loses its last, so a transport can
22
+ * subscribe/unsubscribe upstream in step. Both arguments are supplied so a transport can
23
+ * work at whatever granularity it uses (per-execution, per-bucket).
24
+ */
25
+ private readonly onInterestChange;
26
+ constructor(options?: {
27
+ onInterestChange?: (executionId: string, kind: "first" | "last") => void;
28
+ });
29
+ /**
30
+ * Register a local handler. Returns an unsubscribe that removes it and, when it was the
31
+ * last for that execution, drops the entry (so {@link interest} stays tight).
32
+ */
33
+ add(executionId: string, handler: (event: EventEnvelope) => void): () => void;
34
+ /** Whether anything local cares about this execution. The transport's filter predicate. */
35
+ has(executionId: string): boolean;
36
+ /** The executions with at least one local handler — this node's interest set. */
37
+ interest(): string[];
38
+ /** Number of executions currently subscribed (not the number of handlers). */
39
+ get size(): number;
40
+ /**
41
+ * Fan an event out to local handlers. Synchronous, matching the store contract that a
42
+ * same-process subscriber sees the event as part of the append.
43
+ *
44
+ * Iterates a COPY: a handler that unsubscribes (or subscribes) during delivery must not
45
+ * perturb the set being walked.
46
+ */
47
+ emit(event: EventEnvelope): void;
48
+ /** Drop every subscription. Used by `close()`. */
49
+ clear(): void;
50
+ }
51
+ /**
52
+ * Map an executionId onto one of `buckets` channels, for transports that cannot afford a
53
+ * channel per execution but must not broadcast everything either (ADR 0074 D4).
54
+ *
55
+ * FNV-1a over the id: stable across processes and restarts — which is required, since two
56
+ * nodes must agree on which bucket an execution lives in — and dependency-free.
57
+ */
58
+ export declare function bucketOf(executionId: string, buckets: number): number;
59
+ //# sourceMappingURL=SubscriberRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SubscriberRegistry.d.ts","sourceRoot":"","sources":["../src/SubscriberRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0D;IACnF;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAEnB;gBAGZ,OAAO,GAAE;QAAE,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAA;KAAO;IAK5F;;;OAGG;IACH,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAyB7E,2FAA2F;IAC3F,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIjC,iFAAiF;IACjF,QAAQ,IAAI,MAAM,EAAE;IAIpB,8EAA8E;IAC9E,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;;OAMG;IACH,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAMhC,kDAAkD;IAClD,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CASrE"}
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SubscriberRegistry = void 0;
4
+ exports.bucketOf = bucketOf;
5
+ /**
6
+ * The backend-independent half of `EventStore.subscribe` (ADR 0074 D4).
7
+ *
8
+ * Every store has to do the same three things: keep a per-execution set of local handlers,
9
+ * fan an event out to them, and — for stores whose transport echoes an append back to its
10
+ * own publisher — ignore what it published itself. That was written three times
11
+ * (in-memory, SQLite, Postgres) before this existed.
12
+ *
13
+ * What it deliberately does NOT do is decide how events arrive. That is the genuinely
14
+ * backend-specific part: a global channel, a per-bucket channel, a Redis topic, a Kafka
15
+ * partition. {@link interest} exists so a transport can ask "what am I actually subscribed
16
+ * to?" and narrow what it asks for upstream instead of receiving everything and
17
+ * discarding — the difference between broadcast delivery and interest-filtered delivery.
18
+ *
19
+ * A bring-your-own backend composes this and implements only the transport.
20
+ */
21
+ class SubscriberRegistry {
22
+ handlers = new Map();
23
+ /**
24
+ * Fires when an execution gains its first handler or loses its last, so a transport can
25
+ * subscribe/unsubscribe upstream in step. Both arguments are supplied so a transport can
26
+ * work at whatever granularity it uses (per-execution, per-bucket).
27
+ */
28
+ onInterestChange;
29
+ constructor(options = {}) {
30
+ this.onInterestChange = options.onInterestChange;
31
+ }
32
+ /**
33
+ * Register a local handler. Returns an unsubscribe that removes it and, when it was the
34
+ * last for that execution, drops the entry (so {@link interest} stays tight).
35
+ */
36
+ add(executionId, handler) {
37
+ let set = this.handlers.get(executionId);
38
+ const isFirst = set === undefined;
39
+ if (set === undefined) {
40
+ set = new Set();
41
+ this.handlers.set(executionId, set);
42
+ }
43
+ set.add(handler);
44
+ if (isFirst)
45
+ this.onInterestChange?.(executionId, "first");
46
+ let removed = false;
47
+ return () => {
48
+ // Idempotent: a caller may unsubscribe twice, and must not then fire "last" twice or
49
+ // tear down a subscription a later subscriber re-established.
50
+ if (removed)
51
+ return;
52
+ removed = true;
53
+ const current = this.handlers.get(executionId);
54
+ if (current === undefined)
55
+ return;
56
+ current.delete(handler);
57
+ if (current.size === 0) {
58
+ this.handlers.delete(executionId);
59
+ this.onInterestChange?.(executionId, "last");
60
+ }
61
+ };
62
+ }
63
+ /** Whether anything local cares about this execution. The transport's filter predicate. */
64
+ has(executionId) {
65
+ return this.handlers.has(executionId);
66
+ }
67
+ /** The executions with at least one local handler — this node's interest set. */
68
+ interest() {
69
+ return [...this.handlers.keys()];
70
+ }
71
+ /** Number of executions currently subscribed (not the number of handlers). */
72
+ get size() {
73
+ return this.handlers.size;
74
+ }
75
+ /**
76
+ * Fan an event out to local handlers. Synchronous, matching the store contract that a
77
+ * same-process subscriber sees the event as part of the append.
78
+ *
79
+ * Iterates a COPY: a handler that unsubscribes (or subscribes) during delivery must not
80
+ * perturb the set being walked.
81
+ */
82
+ emit(event) {
83
+ const set = this.handlers.get(event.executionId);
84
+ if (set === undefined || set.size === 0)
85
+ return;
86
+ for (const handler of [...set])
87
+ handler(event);
88
+ }
89
+ /** Drop every subscription. Used by `close()`. */
90
+ clear() {
91
+ this.handlers.clear();
92
+ }
93
+ }
94
+ exports.SubscriberRegistry = SubscriberRegistry;
95
+ /**
96
+ * Map an executionId onto one of `buckets` channels, for transports that cannot afford a
97
+ * channel per execution but must not broadcast everything either (ADR 0074 D4).
98
+ *
99
+ * FNV-1a over the id: stable across processes and restarts — which is required, since two
100
+ * nodes must agree on which bucket an execution lives in — and dependency-free.
101
+ */
102
+ function bucketOf(executionId, buckets) {
103
+ if (buckets <= 1)
104
+ return 0;
105
+ let hash = 0x811c9dc5;
106
+ for (let i = 0; i < executionId.length; i++) {
107
+ hash ^= executionId.charCodeAt(i);
108
+ // 32-bit FNV prime multiply via shifts; `>>> 0` keeps it unsigned.
109
+ hash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;
110
+ }
111
+ return hash % buckets;
112
+ }
113
+ //# sourceMappingURL=SubscriberRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SubscriberRegistry.js","sourceRoot":"","sources":["../src/SubscriberRegistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { ArchiveExecutionPage, ArchiveSegment, ArchiveSegmentHeader, ArchiveStore } from "./archiveSpi";
2
+ /**
3
+ * ADR 0138 H3 - the archive half of tenant scoping (see `TenantScopedEventStore`). Key-prefixes the
4
+ * executionId so a `LogLifecycle` over a tenant-scoped event store, given a shared archive, can only
5
+ * address its own tenant's segments: another tenant's history cannot be read, extended, or (on the
6
+ * plain-delete retention path) deleted, by construction. `LogLifecycle.compact` REFUSES a tenant-scoped
7
+ * event store paired with an archive that is not scoped to the SAME tenant (`tenantScope` is the marker).
8
+ *
9
+ * The sealing markers of a wrapped `EncryptingArchiveStore` (`keys`, `sealsSegments`, `subjectOf`)
10
+ * are forwarded, so the erasure-boundary guard sees through this decorator.
11
+ */
12
+ export declare class TenantScopedArchiveStore implements ArchiveStore {
13
+ private readonly inner;
14
+ private readonly tenant;
15
+ readonly deleteExecution?: NonNullable<ArchiveStore["deleteExecution"]>;
16
+ readonly replaceSegments?: NonNullable<ArchiveStore["replaceSegments"]>;
17
+ constructor(inner: ArchiveStore, tenant: string);
18
+ /** Structural marker: the tenant this archive is scoped to (read by `LogLifecycle.compact`). */
19
+ get tenantScope(): string;
20
+ get keys(): unknown;
21
+ get sealsSegments(): unknown;
22
+ get subjectOf(): unknown;
23
+ private scope;
24
+ append(executionId: string, segment: ArchiveSegment): Promise<void>;
25
+ headers(executionId: string): Promise<ArchiveSegmentHeader[]>;
26
+ read(executionId: string, range?: {
27
+ readonly from?: number;
28
+ readonly through?: number;
29
+ }): Promise<ArchiveSegment[]>;
30
+ listExecutions(opts?: {
31
+ cursor?: string;
32
+ limit?: number;
33
+ }): Promise<ArchiveExecutionPage>;
34
+ close(): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=TenantScopedArchiveStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TenantScopedArchiveStore.d.ts","sourceRoot":"","sources":["../src/TenantScopedArchiveStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACb,MAAM,cAAc,CAAC;AAKtB;;;;;;;;;GASG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAKzD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACxE,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC;gBAGrD,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM;IAkBjC,gGAAgG;IAChG,IAAI,WAAW,IAAI,MAAM,CAExB;IACD,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,aAAa,IAAI,OAAO,CAE3B;IACD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,CAAC,KAAK;IAIb,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAInE,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAI7D,IAAI,CACF,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5D,OAAO,CAAC,cAAc,EAAE,CAAC;IAItB,cAAc,CAClB,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC7C,OAAO,CAAC,oBAAoB,CAAC;IA+BhC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGvB"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TenantScopedArchiveStore = void 0;
4
+ const executionIdOrder_1 = require("./executionIdOrder");
5
+ const SEP = ":"; // the same tenant separator as TenantScopedEventStore / TenantScopedSnapshotStore
6
+ /**
7
+ * ADR 0138 H3 - the archive half of tenant scoping (see `TenantScopedEventStore`). Key-prefixes the
8
+ * executionId so a `LogLifecycle` over a tenant-scoped event store, given a shared archive, can only
9
+ * address its own tenant's segments: another tenant's history cannot be read, extended, or (on the
10
+ * plain-delete retention path) deleted, by construction. `LogLifecycle.compact` REFUSES a tenant-scoped
11
+ * event store paired with an archive that is not scoped to the SAME tenant (`tenantScope` is the marker).
12
+ *
13
+ * The sealing markers of a wrapped `EncryptingArchiveStore` (`keys`, `sealsSegments`, `subjectOf`)
14
+ * are forwarded, so the erasure-boundary guard sees through this decorator.
15
+ */
16
+ class TenantScopedArchiveStore {
17
+ inner;
18
+ tenant;
19
+ deleteExecution;
20
+ replaceSegments;
21
+ constructor(inner, tenant) {
22
+ this.inner = inner;
23
+ this.tenant = tenant;
24
+ if (tenant === "" || tenant.includes(SEP)) {
25
+ throw new TypeError(`TenantScopedArchiveStore: tenant must be non-empty and must not contain the separator "${SEP}" (got ${JSON.stringify(tenant)})`);
26
+ }
27
+ const innerDelete = inner.deleteExecution;
28
+ if (innerDelete !== undefined) {
29
+ this.deleteExecution = (id) => innerDelete.call(inner, this.scope(id));
30
+ }
31
+ const innerReplace = inner.replaceSegments;
32
+ if (innerReplace !== undefined) {
33
+ this.replaceSegments = (id, expected, next) => innerReplace.call(inner, this.scope(id), expected, next);
34
+ }
35
+ }
36
+ /** Structural marker: the tenant this archive is scoped to (read by `LogLifecycle.compact`). */
37
+ get tenantScope() {
38
+ return this.tenant;
39
+ }
40
+ get keys() {
41
+ return this.inner.keys;
42
+ }
43
+ get sealsSegments() {
44
+ return this.inner.sealsSegments;
45
+ }
46
+ get subjectOf() {
47
+ return this.inner.subjectOf;
48
+ }
49
+ scope(id) {
50
+ return `${this.tenant}${SEP}${id}`;
51
+ }
52
+ append(executionId, segment) {
53
+ return this.inner.append(this.scope(executionId), segment);
54
+ }
55
+ headers(executionId) {
56
+ return this.inner.headers(this.scope(executionId));
57
+ }
58
+ read(executionId, range) {
59
+ return this.inner.read(this.scope(executionId), range);
60
+ }
61
+ async listExecutions(opts = {}) {
62
+ const prefix = `${this.tenant}${SEP}`;
63
+ // Seek to the tenant's contiguous id range, and stop at the first id past it.
64
+ let cursor = opts.cursor === undefined ? `${this.tenant}9\u{10FFFF}` : `${prefix}${opts.cursor}`;
65
+ for (;;) {
66
+ const page = await this.inner.listExecutions({
67
+ cursor,
68
+ ...(opts.limit !== undefined && { limit: opts.limit }),
69
+ });
70
+ const executionIds = [];
71
+ let past = false;
72
+ for (const id of page.executionIds) {
73
+ if (id.startsWith(prefix))
74
+ executionIds.push(id.slice(prefix.length));
75
+ else if ((0, executionIdOrder_1.compareExecutionIds)(id, prefix) > 0) {
76
+ past = true;
77
+ break;
78
+ }
79
+ }
80
+ if (past || page.nextCursor === undefined)
81
+ return { executionIds };
82
+ const last = executionIds[executionIds.length - 1];
83
+ if (last !== undefined)
84
+ return { executionIds, nextCursor: last };
85
+ if (page.nextCursor === cursor) {
86
+ throw new TypeError("TenantScopedArchiveStore: listExecutions returned a non-advancing cursor");
87
+ }
88
+ cursor = page.nextCursor;
89
+ }
90
+ }
91
+ close() {
92
+ return this.inner.close();
93
+ }
94
+ }
95
+ exports.TenantScopedArchiveStore = TenantScopedArchiveStore;
96
+ //# sourceMappingURL=TenantScopedArchiveStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TenantScopedArchiveStore.js","sourceRoot":"","sources":["../src/TenantScopedArchiveStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { EventEnvelope } from "@loomwork/core";
2
+ import type { EventStore } from "./types";
3
+ /**
4
+ * K12.5 — a tenant-scoped view of a shared {@link EventStore}. Every execution
5
+ * id is key-prefixed with the tenant on the way in and un-prefixed on the way
6
+ * out, so a store scoped to tenant A can only ever ADDRESS keys under A —
7
+ * reading or subscribing to tenant B's executions is impossible **by
8
+ * construction**, not by a filter that could be bypassed. The app sees its own
9
+ * unprefixed ids throughout (append/read/subscribe/fork all round-trip).
10
+ *
11
+ * Default scoping mechanism per the amendment (A7): key-prefixing. Postgres
12
+ * row-level security is an optional hardening layer on top, not a replacement.
13
+ */
14
+ export declare class TenantScopedEventStore implements EventStore {
15
+ private readonly inner;
16
+ private readonly tenant;
17
+ readonly appendBatch?: NonNullable<EventStore["appendBatch"]>;
18
+ readonly head?: NonNullable<EventStore["head"]>;
19
+ readonly readRange?: NonNullable<EventStore["readRange"]>;
20
+ readonly acquireFence?: NonNullable<EventStore["acquireFence"]>;
21
+ readonly acquireFenceIf?: NonNullable<EventStore["acquireFenceIf"]>;
22
+ readonly appendIfUnchanged?: NonNullable<EventStore["appendIfUnchanged"]>;
23
+ readonly storeNow?: NonNullable<EventStore["storeNow"]>;
24
+ readonly sealingCapability?: NonNullable<EventStore["sealingCapability"]>;
25
+ readonly readAtRest?: NonNullable<EventStore["readAtRest"]>;
26
+ readonly copyPrefix?: NonNullable<EventStore["copyPrefix"]>;
27
+ readonly deleteExecution?: NonNullable<EventStore["deleteExecution"]>;
28
+ readonly deleteThrough?: NonNullable<EventStore["deleteThrough"]>;
29
+ readonly truncatePrefix?: NonNullable<EventStore["truncatePrefix"]>;
30
+ readonly truncatePrefixKeeping?: NonNullable<EventStore["truncatePrefixKeeping"]>;
31
+ readonly listExecutions?: NonNullable<EventStore["listExecutions"]>;
32
+ readonly listExecutionsPage?: NonNullable<EventStore["listExecutionsPage"]>;
33
+ constructor(inner: EventStore, tenant: string);
34
+ /** Structural marker: the tenant this view is scoped to (`LogLifecycle.compact` pairs it with the archive's). */
35
+ get tenantScope(): string;
36
+ private scope;
37
+ /** Rewrite a stored envelope's scoped executionId/id back to the app's view. */
38
+ private unscope;
39
+ /** Prefix an envelope for storage under the tenant scope. */
40
+ private scopeEnvelope;
41
+ append(envelope: EventEnvelope): Promise<void>;
42
+ /**
43
+ * L20 — the store's write refusals name the id it WROTE, which is the scoped one (`acme:x`).
44
+ * The app never sees scoped ids anywhere else, and callers key on the refusal's id: a
45
+ * dispatcher decides "another holder owns THIS execution" by comparing it with its own
46
+ * (`isSupersededRefusal`), so a prefixed id made every fenced-out attempt read as a genuine
47
+ * failure and the ping-pong came back under tenancy. The three normalized refusals are
48
+ * re-raised as the same class and name with the ids unscoped and the original as `cause`.
49
+ * Anything else — and a refusal naming another tenant's key, which cannot be unscoped — passes
50
+ * through untouched.
51
+ */
52
+ private readonly rethrowUnscoped;
53
+ private unscopeError;
54
+ read(executionId: string): Promise<EventEnvelope[]>;
55
+ readFrom(executionId: string, fromSequence: number): Promise<EventEnvelope[]>;
56
+ subscribe(executionId: string, handler: (event: EventEnvelope) => void): Promise<() => void>;
57
+ close(): Promise<void>;
58
+ }
59
+ //# sourceMappingURL=TenantScopedEventStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TenantScopedEventStore.d.ts","sourceRoot":"","sources":["../src/TenantScopedEventStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,OAAO,KAAK,EAAE,UAAU,EAA2C,MAAM,SAAS,CAAC;AA6EnF;;;;;;;;;;GAUG;AACH,qBAAa,sBAAuB,YAAW,UAAU;IAmBrD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAnBzB,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC;IAChE,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAClF,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAC;gBAGzD,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM;IA+DjC,iHAAiH;IACjH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,OAAO,CAAC,KAAK;IAIb,gFAAgF;IAChF,OAAO,CAAC,OAAO;IAOf,6DAA6D;IAC7D,OAAO,CAAC,aAAa;IAIf,MAAM,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD;;;;;;;;;OASG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE9B;IAEF,OAAO,CAAC,YAAY;IA8Bd,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAInD,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAM7E,SAAS,CACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GACtC,OAAO,CAAC,MAAM,IAAI,CAAC;IAIhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TenantScopedEventStore = void 0;
4
+ const executionIdOrder_1 = require("./executionIdOrder");
5
+ const fencing_1 = require("./fencing");
6
+ const installOptional_1 = require("./installOptional");
7
+ const writeConflicts_1 = require("./writeConflicts");
8
+ // The tenant/execution key separator — a control char that can't appear in a
9
+ // normal executionId, so prefixing is unambiguous and un-stripping is exact.
10
+ const SEP = ":"; // ":" is not in the ULID alphabet — an unambiguous, collision-free tenant separator
11
+ /**
12
+ * One tenant's page of a shared store's keyset-paged enumeration.
13
+ *
14
+ * The cursor is an OUTER id — this tenant's unprefixed executionId — because that is what a
15
+ * caller holds: `ShardedEventStore` (the ADR 0074 composition, sharding outermost) resumes
16
+ * every shard from the last id of its MERGED page, which it only ever sees unprefixed. Before
17
+ * L10 this passed the inner, prefixed cursor out, and got the unprefixed one back in, so
18
+ * `Sharded(TenantScoped(…))` compared `e-04` against `acme:e-…` and re-served its first page
19
+ * forever.
20
+ *
21
+ * Since EventStore SPI 2.0.0 every store pages in code point order, where the ids under
22
+ * `prefix` are CONTIGUOUS. So a page seeks to the tenant (the empty id `prefix` included —
23
+ * a blank stream is still this tenant's, and repair must be able to find it) and stops at
24
+ * the first id past the prefix, instead of walking the whole shared store. The cursor is
25
+ * exclusive, so a fresh scan cannot start AT `prefix`; it starts at {@link seekBelow}, the
26
+ * largest practical string below `prefix` (`acme9\u{10FFFF}` for `acme:`): every other
27
+ * tenant whose name extends this one with a character below `:` (`acme-eu`, `acme.io`,
28
+ * `acme0`…`acme9`) sorts before it and is never read.
29
+ *
30
+ * It does not stop early with an empty page and a cursor: a non-final page with no ids is not
31
+ * something consumers are written for (`ShardedEventStore` reads an empty merged page as the
32
+ * end). Memory stays one inner page.
33
+ */
34
+ /**
35
+ * The exclusive cursor a fresh tenant scan starts from: `SEP` is `:` (U+003A), so the string
36
+ * just below `tenant + ":"` is `tenant + "9"` (U+0039) followed by the highest code point.
37
+ * Only an id that continues past `tenant + "9\u{10FFFF}"` (never produced by an id
38
+ * generator) could sit between it and the tenant's range, and would be walked and dropped.
39
+ * The tenant's blank stream (`tenant + ":"`) sorts after it, so repair still finds it.
40
+ */
41
+ function seekBelow(tenant) {
42
+ return `${tenant}9\u{10FFFF}`;
43
+ }
44
+ async function pageWithinPrefix(inner, tenant, prefix, opts) {
45
+ let cursor = opts?.cursor === undefined ? seekBelow(tenant) : `${prefix}${opts.cursor}`;
46
+ for (;;) {
47
+ const page = await inner.listExecutionsPage({
48
+ cursor,
49
+ ...(opts?.limit !== undefined && { limit: opts.limit }),
50
+ });
51
+ const executionIds = [];
52
+ let pastPrefix = false;
53
+ for (const id of page.executionIds) {
54
+ if (id.startsWith(prefix))
55
+ executionIds.push(id.slice(prefix.length));
56
+ // Every id after the tenant's range sorts after `prefix` without starting with it.
57
+ else if ((0, executionIdOrder_1.compareExecutionIds)(id, prefix) > 0) {
58
+ pastPrefix = true;
59
+ break;
60
+ }
61
+ }
62
+ const last = executionIds[executionIds.length - 1];
63
+ if (pastPrefix || page.nextCursor === undefined)
64
+ return { executionIds };
65
+ if (last !== undefined)
66
+ return { executionIds, nextCursor: last };
67
+ if (page.nextCursor === cursor) {
68
+ // The same guard `iterateExecutionIds` applies — except this loop is internal, so
69
+ // without it a store bug would spin here where no caller could see it.
70
+ throw new TypeError("TenantScopedEventStore: listExecutionsPage returned a non-advancing cursor (store bug)");
71
+ }
72
+ cursor = page.nextCursor;
73
+ }
74
+ }
75
+ /**
76
+ * K12.5 — a tenant-scoped view of a shared {@link EventStore}. Every execution
77
+ * id is key-prefixed with the tenant on the way in and un-prefixed on the way
78
+ * out, so a store scoped to tenant A can only ever ADDRESS keys under A —
79
+ * reading or subscribing to tenant B's executions is impossible **by
80
+ * construction**, not by a filter that could be bypassed. The app sees its own
81
+ * unprefixed ids throughout (append/read/subscribe/fork all round-trip).
82
+ *
83
+ * Default scoping mechanism per the amendment (A7): key-prefixing. Postgres
84
+ * row-level security is an optional hardening layer on top, not a replacement.
85
+ */
86
+ class TenantScopedEventStore {
87
+ inner;
88
+ tenant;
89
+ appendBatch;
90
+ head;
91
+ readRange;
92
+ acquireFence;
93
+ acquireFenceIf;
94
+ appendIfUnchanged;
95
+ storeNow;
96
+ sealingCapability;
97
+ readAtRest;
98
+ copyPrefix;
99
+ deleteExecution;
100
+ deleteThrough;
101
+ truncatePrefix;
102
+ truncatePrefixKeeping;
103
+ listExecutions;
104
+ listExecutionsPage;
105
+ constructor(inner, tenant) {
106
+ this.inner = inner;
107
+ this.tenant = tenant;
108
+ if (tenant === "" || tenant.includes(SEP)) {
109
+ // Prefix scoping is only isolation if no tenant's prefix is a prefix of another's:
110
+ // tenant "a" scopes "b:x" to "a:b:x", which is tenant "a:b"'s "x".
111
+ throw new TypeError(`TenantScopedEventStore: tenant must be non-empty and must not contain the separator "${SEP}" (got ${JSON.stringify(tenant)})`);
112
+ }
113
+ const scope = (id) => `${this.tenant}${SEP}${id}`;
114
+ const prefix = `${this.tenant}${SEP}`;
115
+ // Every optional method decided here, over the whole union (ADR 0074 Amendment): a new
116
+ // SPI method is a compile error until this decorator forwards, overrides or refuses it.
117
+ const plan = {
118
+ // Order preserved: the SPI's per-execution order is array order.
119
+ appendBatch: (envs) => inner.appendBatch(envs.map((e) => this.scopeEnvelope(e))).catch(this.rethrowUnscoped),
120
+ head: (id) => inner.head(scope(id)),
121
+ readRange: (id, from, to) => inner.readRange(scope(id), from, to).then((evs) => evs.map((e) => this.unscope(e))),
122
+ acquireFence: (id) => inner.acquireFence(scope(id)),
123
+ acquireFenceIf: (id, expect) => inner.acquireFenceIf(scope(id), expect),
124
+ appendIfUnchanged: (id, event, expect) => inner.appendIfUnchanged(scope(id), event === null ? null : this.scopeEnvelope(event), expect).catch(this.rethrowUnscoped),
125
+ storeNow: "forward", // the store clock is not tenant data
126
+ sealingCapability: "forward", // what the store seals is not tenant data
127
+ // The at-rest view is tenant-scoped like `read`: ids scoped on the way in, unscoped on the way out.
128
+ readAtRest: () => {
129
+ const raw = inner.readAtRest();
130
+ const back = async (p) => (await p).map((e) => this.unscope(e));
131
+ return {
132
+ read: (id) => back(raw.read(scope(id))),
133
+ readFrom: (id, from) => back(raw.readFrom(scope(id), from)),
134
+ ...(raw.readRange !== undefined && {
135
+ readRange: (id, a, b) => back(raw.readRange(scope(id), a, b)),
136
+ }),
137
+ ...(raw.head !== undefined && { head: (id) => raw.head(scope(id)) }),
138
+ };
139
+ },
140
+ // Fork stays IN-TENANT: both ids are scoped, so a fork can never cross the
141
+ // tenant boundary (a cross-tenant copy is unaddressable here).
142
+ copyPrefix: (from, to, through, metadata) => inner.copyPrefix(scope(from), scope(to), through, metadata).catch(this.rethrowUnscoped),
143
+ deleteExecution: (id) => inner.deleteExecution(scope(id)),
144
+ deleteThrough: (id, through) => inner.deleteThrough(scope(id), through),
145
+ truncatePrefix: (id, through) => inner.truncatePrefix(scope(id), through),
146
+ truncatePrefixKeeping: (id, through, keep) => inner.truncatePrefixKeeping(scope(id), through, keep),
147
+ // Enumerate only THIS tenant's executions: keep the ones under our prefix, strip it.
148
+ // Cross-tenant ids are invisible (isolation by construction, K12.5).
149
+ listExecutions: async () => (await inner.listExecutions())
150
+ .filter((id) => id.startsWith(prefix))
151
+ .map((id) => id.slice(prefix.length)),
152
+ listExecutionsPage: (opts) => pageWithinPrefix(inner, this.tenant, prefix, opts),
153
+ };
154
+ (0, installOptional_1.installOptional)(this, inner, plan);
155
+ }
156
+ /** Structural marker: the tenant this view is scoped to (`LogLifecycle.compact` pairs it with the archive's). */
157
+ get tenantScope() {
158
+ return this.tenant;
159
+ }
160
+ scope(id) {
161
+ return `${this.tenant}${SEP}${id}`;
162
+ }
163
+ /** Rewrite a stored envelope's scoped executionId/id back to the app's view. */
164
+ unscope(e) {
165
+ const prefix = `${this.tenant}${SEP}`;
166
+ if (!e.executionId.startsWith(prefix))
167
+ return e;
168
+ const executionId = e.executionId.slice(prefix.length);
169
+ return { ...e, executionId, id: e.id.startsWith(prefix) ? e.id.slice(prefix.length) : e.id };
170
+ }
171
+ /** Prefix an envelope for storage under the tenant scope. */
172
+ scopeEnvelope(e) {
173
+ return { ...e, executionId: this.scope(e.executionId), id: this.scope(e.id) };
174
+ }
175
+ async append(envelope) {
176
+ return this.inner.append(this.scopeEnvelope(envelope)).catch(this.rethrowUnscoped);
177
+ }
178
+ /**
179
+ * L20 — the store's write refusals name the id it WROTE, which is the scoped one (`acme:x`).
180
+ * The app never sees scoped ids anywhere else, and callers key on the refusal's id: a
181
+ * dispatcher decides "another holder owns THIS execution" by comparing it with its own
182
+ * (`isSupersededRefusal`), so a prefixed id made every fenced-out attempt read as a genuine
183
+ * failure and the ping-pong came back under tenancy. The three normalized refusals are
184
+ * re-raised as the same class and name with the ids unscoped and the original as `cause`.
185
+ * Anything else — and a refusal naming another tenant's key, which cannot be unscoped — passes
186
+ * through untouched.
187
+ */
188
+ rethrowUnscoped = (err) => {
189
+ throw this.unscopeError(err);
190
+ };
191
+ unscopeError(err) {
192
+ if (err === null || typeof err !== "object")
193
+ return err;
194
+ const prefix = `${this.tenant}${SEP}`;
195
+ const e = err;
196
+ const executionId = e.executionId;
197
+ if (typeof executionId !== "string" || !executionId.startsWith(prefix))
198
+ return err;
199
+ const id = executionId.slice(prefix.length);
200
+ switch (e.name) {
201
+ case "StaleFenceError":
202
+ return new fencing_1.StaleFenceError(id, e.staleFence, e.currentFence, err);
203
+ case "DuplicateSequenceError":
204
+ return new writeConflicts_1.DuplicateSequenceError(id, e.sequence, err);
205
+ case "DuplicateEnvelopeIdError": {
206
+ const envelopeId = String(e.envelopeId);
207
+ return new writeConflicts_1.DuplicateEnvelopeIdError(envelopeId.startsWith(prefix) ? envelopeId.slice(prefix.length) : envelopeId, id, err);
208
+ }
209
+ default:
210
+ return err;
211
+ }
212
+ }
213
+ async read(executionId) {
214
+ return (await this.inner.read(this.scope(executionId))).map((e) => this.unscope(e));
215
+ }
216
+ async readFrom(executionId, fromSequence) {
217
+ return (await this.inner.readFrom(this.scope(executionId), fromSequence)).map((e) => this.unscope(e));
218
+ }
219
+ async subscribe(executionId, handler) {
220
+ return this.inner.subscribe(this.scope(executionId), (e) => handler(this.unscope(e)));
221
+ }
222
+ async close() {
223
+ return this.inner.close();
224
+ }
225
+ }
226
+ exports.TenantScopedEventStore = TenantScopedEventStore;
227
+ //# sourceMappingURL=TenantScopedEventStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TenantScopedEventStore.js","sourceRoot":"","sources":["../src/TenantScopedEventStore.ts"],"names":[],"mappings":"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"}