@kairos-es/core 1.0.0-alpha.4 → 1.0.0-alpha.5

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 (113) hide show
  1. package/README.md +3 -3
  2. package/dist/cjs/AppendCondition.js +1 -3
  3. package/dist/cjs/AppendCondition.js.map +1 -1
  4. package/dist/cjs/AppendIsOwned.js +6 -6
  5. package/dist/cjs/AppendIsOwned.js.map +1 -1
  6. package/dist/cjs/ContractStore.js +2 -2
  7. package/dist/cjs/ContractStore.js.map +1 -1
  8. package/dist/cjs/DcbEventStore.js +1 -1
  9. package/dist/cjs/DcbEventStore.js.map +1 -1
  10. package/dist/cjs/Event.js +29 -13
  11. package/dist/cjs/Event.js.map +1 -1
  12. package/dist/cjs/Query.js +1 -1
  13. package/dist/cjs/Query.js.map +1 -1
  14. package/dist/cjs/SubscribeMachine.js +20 -22
  15. package/dist/cjs/SubscribeMachine.js.map +1 -1
  16. package/dist/cjs/inMemory.js +4 -4
  17. package/dist/cjs/inMemory.js.map +1 -1
  18. package/dist/cjs/retryOnConflict.js +4 -10
  19. package/dist/cjs/retryOnConflict.js.map +1 -1
  20. package/dist/cjs/testing.js +2 -2
  21. package/dist/cjs/testing.js.map +1 -1
  22. package/dist/{dts → dts-cjs}/AppendCondition.d.ts +5 -7
  23. package/dist/{dts → dts-cjs}/AppendCondition.d.ts.map +1 -1
  24. package/dist/{dts → dts-cjs}/AppendIsOwned.d.ts +6 -6
  25. package/dist/{dts → dts-cjs}/AppendIsOwned.d.ts.map +1 -1
  26. package/dist/{dts → dts-cjs}/ContractStore.d.ts +3 -2
  27. package/dist/dts-cjs/ContractStore.d.ts.map +1 -0
  28. package/dist/{dts → dts-cjs}/DcbEventStore.d.ts +2 -2
  29. package/dist/{dts → dts-cjs}/DcbEventStore.d.ts.map +1 -1
  30. package/dist/{dts → dts-cjs}/Event.d.ts +31 -20
  31. package/dist/dts-cjs/Event.d.ts.map +1 -0
  32. package/dist/{dts → dts-cjs}/Query.d.ts +5 -5
  33. package/dist/{dts → dts-cjs}/SubscribeMachine.d.ts +11 -15
  34. package/dist/dts-cjs/SubscribeMachine.d.ts.map +1 -0
  35. package/dist/{dts → dts-cjs}/inMemory.d.ts.map +1 -1
  36. package/dist/{dts → dts-cjs}/index.d.ts +1 -1
  37. package/dist/dts-cjs/package.json +3 -0
  38. package/dist/{dts → dts-cjs}/retryOnConflict.d.ts +1 -7
  39. package/dist/dts-cjs/retryOnConflict.d.ts.map +1 -0
  40. package/dist/{dts → dts-cjs}/testing.d.ts +1 -1
  41. package/dist/dts-esm/AppendCondition.d.ts +21 -0
  42. package/dist/dts-esm/AppendCondition.d.ts.map +1 -0
  43. package/dist/dts-esm/AppendIsOwned.d.ts +78 -0
  44. package/dist/dts-esm/AppendIsOwned.d.ts.map +1 -0
  45. package/dist/dts-esm/ContractStore.d.ts +62 -0
  46. package/dist/dts-esm/ContractStore.d.ts.map +1 -0
  47. package/dist/dts-esm/DcbEventStore.d.ts +64 -0
  48. package/dist/dts-esm/DcbEventStore.d.ts.map +1 -0
  49. package/dist/dts-esm/Event.d.ts +174 -0
  50. package/dist/dts-esm/Event.d.ts.map +1 -0
  51. package/dist/dts-esm/Projection.d.ts +124 -0
  52. package/dist/dts-esm/Projection.d.ts.map +1 -0
  53. package/dist/dts-esm/Query.d.ts +180 -0
  54. package/dist/dts-esm/Query.d.ts.map +1 -0
  55. package/dist/dts-esm/SubscribeMachine.d.ts +175 -0
  56. package/dist/dts-esm/SubscribeMachine.d.ts.map +1 -0
  57. package/dist/dts-esm/errors.d.ts +17 -0
  58. package/dist/dts-esm/errors.d.ts.map +1 -0
  59. package/dist/dts-esm/inMemory.d.ts +76 -0
  60. package/dist/dts-esm/inMemory.d.ts.map +1 -0
  61. package/dist/dts-esm/index.d.ts +40 -0
  62. package/dist/dts-esm/index.d.ts.map +1 -0
  63. package/dist/dts-esm/package.json +3 -0
  64. package/dist/dts-esm/retryOnConflict.d.ts +64 -0
  65. package/dist/dts-esm/retryOnConflict.d.ts.map +1 -0
  66. package/dist/dts-esm/testing.d.ts +39 -0
  67. package/dist/dts-esm/testing.d.ts.map +1 -0
  68. package/dist/esm/AppendCondition.js +1 -3
  69. package/dist/esm/AppendCondition.js.map +1 -1
  70. package/dist/esm/AppendIsOwned.js +6 -6
  71. package/dist/esm/AppendIsOwned.js.map +1 -1
  72. package/dist/esm/ContractStore.js +2 -2
  73. package/dist/esm/ContractStore.js.map +1 -1
  74. package/dist/esm/DcbEventStore.js +1 -1
  75. package/dist/esm/DcbEventStore.js.map +1 -1
  76. package/dist/esm/Event.js +30 -14
  77. package/dist/esm/Event.js.map +1 -1
  78. package/dist/esm/Query.js +1 -1
  79. package/dist/esm/Query.js.map +1 -1
  80. package/dist/esm/SubscribeMachine.js +21 -23
  81. package/dist/esm/SubscribeMachine.js.map +1 -1
  82. package/dist/esm/inMemory.js +5 -5
  83. package/dist/esm/inMemory.js.map +1 -1
  84. package/dist/esm/index.js +1 -1
  85. package/dist/esm/retryOnConflict.js +4 -10
  86. package/dist/esm/retryOnConflict.js.map +1 -1
  87. package/dist/esm/testing.js +2 -2
  88. package/dist/esm/testing.js.map +1 -1
  89. package/package.json +21 -11
  90. package/src/AppendCondition.ts +1 -1
  91. package/src/AppendIsOwned.ts +7 -7
  92. package/src/ContractStore.ts +5 -4
  93. package/src/DcbEventStore.ts +3 -3
  94. package/src/Event.ts +49 -14
  95. package/src/Query.ts +1 -1
  96. package/src/SubscribeMachine.ts +38 -31
  97. package/src/inMemory.ts +6 -6
  98. package/src/index.ts +1 -1
  99. package/src/retryOnConflict.ts +6 -11
  100. package/src/testing.ts +2 -2
  101. package/testing/package.json +1 -1
  102. package/dist/dts/ContractStore.d.ts.map +0 -1
  103. package/dist/dts/Event.d.ts.map +0 -1
  104. package/dist/dts/SubscribeMachine.d.ts.map +0 -1
  105. package/dist/dts/retryOnConflict.d.ts.map +0 -1
  106. /package/dist/{dts → dts-cjs}/Projection.d.ts +0 -0
  107. /package/dist/{dts → dts-cjs}/Projection.d.ts.map +0 -0
  108. /package/dist/{dts → dts-cjs}/Query.d.ts.map +0 -0
  109. /package/dist/{dts → dts-cjs}/errors.d.ts +0 -0
  110. /package/dist/{dts → dts-cjs}/errors.d.ts.map +0 -0
  111. /package/dist/{dts → dts-cjs}/inMemory.d.ts +0 -0
  112. /package/dist/{dts → dts-cjs}/index.d.ts.map +0 -0
  113. /package/dist/{dts → dts-cjs}/testing.d.ts.map +0 -0
@@ -0,0 +1,175 @@
1
+ /**
2
+ * The subscribe machine every engine's `subscribe` is built from — the in-memory
3
+ * store, `@kairos-es/store-postgres` and `@kairos-es/store-sqlite`: the wake queue,
4
+ * the wake-source drain, the paged catch-up reconcile, the `> boundary` de-dup (via
5
+ * the cursor's exclusive lower bound) and the poll loop that together turn a paged
6
+ * read into a live subscription.
7
+ *
8
+ * It lives in core because delivery semantics are contract semantics. An engine
9
+ * supplies TRANSPORT through two STUBBABLE seams — `fetch`, a paged read strictly
10
+ * after a cursor, and `listen`, an optional wake source — and inherits the delivery
11
+ * behaviour whole, exactly as it supplies mechanics to `makeContractStore` and
12
+ * inherits the contract policy whole. Two engines that each grew their own wake
13
+ * loop would be identical only by inspection, and `subscribe` is the verb where
14
+ * that is least affordable: a divergence there loses events rather than failing.
15
+ * Both seams take plain in-memory values in a test, so the wake/poll/de-dup logic
16
+ * is exercisable with no live backend at all.
17
+ *
18
+ * All THREE are the callers, and the count matters to a reader here: it means there
19
+ * is exactly ONE delivery algorithm in this repo, and it is the code below. The
20
+ * in-memory store is a caller like the rest — it hands in a `fetch` over its log
21
+ * `Ref` and its append `PubSub` as a wake source and keeps no delivery logic of its
22
+ * own — which is what makes the shared contract suite's subscribe cases a comparison
23
+ * of two BACKENDS rather than of two delivery algorithms.
24
+ *
25
+ * Correctness rests on the periodic position-based reconcile — a re-read from the
26
+ * cursor every poll interval — and NEVER on the wake source, which only lowers
27
+ * delivery latency below that interval. A subscription with no wake source at all
28
+ * is therefore complete and correct, merely slower, which is why `listen` is
29
+ * optional in the TYPE rather than by the convention of passing `Stream.never`:
30
+ * poll-only delivery is a supported shape, not a degraded one.
31
+ *
32
+ * ## The precondition every engine inherits: ASCENDING COMMIT ORDER
33
+ *
34
+ * A position that is committed and VISIBLE must never be below one already
35
+ * delivered. Gaps are fine: the cursor is an ascending exclusive lower bound, so a
36
+ * number that is allocated and never committed, or never allocated at all, is
37
+ * simply a number the machine never sees. Out-of-order commits are not fine. If one
38
+ * writer allocates position 5 and another allocates 6, and 6 commits first, a poll
39
+ * landing between the two commits delivers 6 and advances the cursor past it — and
40
+ * 5 is then lost forever, because no later read looks below the cursor and nothing
41
+ * anywhere notices the hole. An engine whose concurrent writers can commit out of
42
+ * order MUST serialise them, or this machine will silently skip. An engine that
43
+ * takes an exclusive lock on both of its append paths discharges the obligation
44
+ * that way; an engine whose append is a single statement discharges it
45
+ * structurally.
46
+ *
47
+ * The obligation is written down HERE, in the algorithm that depends on it, rather
48
+ * than in whichever engine happens to satisfy it. It is a property of this delivery
49
+ * loop and of nothing else, so an engine author reading only their own source has
50
+ * no way to discover that anything downstream needs it — and the failure mode is
51
+ * the worst kind there is: no error, no defect, no failing test, just an event that
52
+ * a subscriber never receives. A single engine owning both its append path and its
53
+ * own wake loop can leave the assumption implicit; a shared machine cannot.
54
+ *
55
+ * ## `catchUpPageSize` bounds the READ, not the emission
56
+ *
57
+ * `drainFrom` below PAGES the catch-up read — `catchUpPageSize` bounds each
58
+ * `fetch` — but it COLLECTS every page into one array, which is emitted as a SINGLE
59
+ * chunk. So peak memory at subscription start is O(backlog) rather than O(page): a
60
+ * subscriber joining behind a large log materialises the whole backlog before its
61
+ * consumer sees the first element.
62
+ *
63
+ * That is a memory characteristic and nothing more, which is why ONE chunk is
64
+ * enough here: downstream commit boundaries do not depend on the upstream chunking
65
+ * at all, because the read side's `Stream.groupedWithin` re-chunks the stream into
66
+ * bounded micro-batches whatever arrives (ADR-0007), so a projection's transactions
67
+ * stay small even against a one-chunk catch-up. The cost therefore lands on backlog
68
+ * memory alone, and it lands hardest on the durable-log-with-in-memory-view mode,
69
+ * where an ephemeral view re-catches-up the entire log on every boot.
70
+ */
71
+ import { Effect, Schema, type Scope, Stream } from 'effect';
72
+ import type { Position, SequencedEvent } from './Event.js';
73
+ import { ReadLimit } from './Event.js';
74
+ /**
75
+ * A short catch-up page size default for the reconcile loop. Reads page by page
76
+ * until a short page signals the tail is drained, so a large backlog is streamed in
77
+ * bounded chunks rather than one unbounded read. Every engine takes its
78
+ * `catchUpPageSize` from here — each SQL engine as its config's default, the
79
+ * in-memory store as its options' — so backends cannot drift on tuning.
80
+ */
81
+ export declare const DEFAULT_CATCH_UP_PAGE_SIZE = 512;
82
+ /**
83
+ * The default upper bound a subscriber waits for a wake before re-reading from its
84
+ * cursor ANYWAY (milliseconds). Every engine takes its `pollInterval` from here —
85
+ * each SQL engine as its config's default, the in-memory store as its options' — so
86
+ * backends cannot drift on tuning.
87
+ *
88
+ * This periodic position-based poll — not the wake source — IS the correctness
89
+ * guarantee: every committed position is reconciled within this interval whether or
90
+ * not a wake for it was ever heard. That covers a wake fired before the source was
91
+ * fully established, a coalesced or dropped wake, and a wake source that died
92
+ * silently and never said so. A wake source only lowers delivery latency BELOW this
93
+ * interval; it never carries delivery, so no engine's wake mechanism can make the
94
+ * right figure here engine-specific. It trades worst-case delivery latency against
95
+ * idle read volume and nothing else. Upstream `postgres_tt.py` picks the same
96
+ * one-second fallback poll (`conn.wait(..., interval=1)`).
97
+ */
98
+ export declare const DEFAULT_POLL_INTERVAL_MILLIS = 1000;
99
+ /**
100
+ * The two subscribe-tuning config FIELDS, for an engine whose config is a `Schema`
101
+ * to spread into its own struct.
102
+ *
103
+ * These knobs are the machine's, not any engine's: they name what the loop above
104
+ * does, the figures they fall back to are the two constants declared beside them,
105
+ * and neither is derivable from a backend's own properties — which is why the
106
+ * defaults already live here. Shipping the FIELD rather than only the default is
107
+ * what closes the last gap in that arrangement. An engine that spreads these gets
108
+ * the key NAME, the VALIDATION and the DEFAULT from one place, so a caller who has
109
+ * tuned one engine's subscribe reads another's without translating, and the two
110
+ * cannot drift in what they ACCEPT any more than in what they assume. Two engines
111
+ * hand-authoring the identical `Schema.optionalKey(…)` twice were identical only
112
+ * by inspection.
113
+ *
114
+ * The bounds differ between the two because the quantities do. `pollInterval` is a
115
+ * duration, so a fraction of a millisecond is meaningful and only `isGreaterThan(0)`
116
+ * and `isFinite` matter: at zero or below the loop would spin without ever waiting for a
117
+ * wake, and at `Infinity` the periodic re-read — the half that carries delivery —
118
+ * would never come, which silently converts the wake source from an optimisation
119
+ * into the guarantee. `catchUpPageSize` is a COUNT, so it must be an integer
120
+ * (`Schema.Int` excludes `Infinity` with it): the reconcile loop compares a page's
121
+ * length against it to decide the tail is drained, and a page of zero would report
122
+ * "drained" forever without reading anything.
123
+ *
124
+ * `optionalKey` on both keeps the decoded type at `key?: T` rather than
125
+ * `T | undefined`, matching the repo's `exactOptionalPropertyTypes` discipline — an
126
+ * absent key, never an explicit `undefined`.
127
+ */
128
+ export declare const subscriptionTuningFields: {
129
+ readonly pollInterval: Schema.optionalKey<Schema.Number>;
130
+ readonly catchUpPageSize: Schema.optionalKey<Schema.Int>;
131
+ };
132
+ /**
133
+ * The two seams the subscribe machine runs over, plus the two tuning knobs. `E` is
134
+ * the engine's own infrastructure error — whatever its reads fail with — and it
135
+ * stays on the channel through to `makeContractStore`, which dies it.
136
+ *
137
+ * ONE type parameter and not two, deliberately: a wake source with an error type of
138
+ * its own is `Stream.orDie`d by the caller, yielding a `Stream<unknown, never>`
139
+ * that is assignable here under Effect's covariant error channel. A second
140
+ * parameter would buy nothing and would have to be spelled at every call site. Nor
141
+ * is there an `R`: neither seam contributes a requirement, and unused generality is
142
+ * not free.
143
+ */
144
+ export interface SubscriptionDeps<E> {
145
+ /**
146
+ * Page-read the events strictly after `from`, up to `limit`, ascending by
147
+ * position. Returning FEWER than `limit` signals the tail is drained.
148
+ */
149
+ readonly fetch: (from: Position, limit: ReadLimit) => Effect.Effect<ReadonlyArray<SequencedEvent>, E>;
150
+ /**
151
+ * The raw wake source: each element is an OPAQUE wake signal. The machine taps it
152
+ * into its own wake queue and drains it on a background fibre; it never reads the
153
+ * elements, so anything an engine can push on a change will do.
154
+ *
155
+ * OPTIONAL, and omitting it defaults to `Stream.never` at the point of use — an
156
+ * engine with no wake mechanism gets poll-only delivery, which the poll loop
157
+ * already makes correct. Under `exactOptionalPropertyTypes` that means omitting
158
+ * the key entirely rather than passing `undefined`.
159
+ */
160
+ readonly listen?: Stream.Stream<unknown, E>;
161
+ /** The poll interval in milliseconds (see `DEFAULT_POLL_INTERVAL_MILLIS`). */
162
+ readonly pollInterval: number;
163
+ /** The catch-up page size (see `DEFAULT_CATCH_UP_PAGE_SIZE`). */
164
+ readonly catchUpPageSize: number;
165
+ }
166
+ /**
167
+ * Build the subscription `Stream`: catch up from `after`, then track the live tail.
168
+ * The cursor threads through the `Stream.paginate` state, starting at `after` and
169
+ * advancing to the last delivered position each batch; the stream never completes
170
+ * on its own (the next state is always `Option.some`), so the enclosing scope
171
+ * interrupts it on close. An `E` mid-stream stays on the channel; `makeContractStore` dies it
172
+ * (subscribe is `E = never` on the contract).
173
+ */
174
+ export declare const subscribeStream: <E>(deps: SubscriptionDeps<E>, after: Position | undefined) => Stream.Stream<SequencedEvent, E, Scope.Scope>;
175
+ //# sourceMappingURL=SubscribeMachine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SubscribeMachine.d.ts","sourceRoot":"","sources":["../../src/SubscribeMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,OAAO,EACL,MAAM,EAIN,MAAM,EACN,KAAK,KAAK,EACV,MAAM,EACP,MAAM,QAAQ,CAAA;AACf,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACvD,OAAO,EAAU,SAAS,EAAE,MAAM,SAAS,CAAA;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,wBAAwB;;;CAO3B,CAAA;AAiBV;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,SAAS,KACb,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAA;IACpD;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC3C,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,MAAM,gBAAgB,CAAC,CAAC,CAAC,EACzB,OAAO,QAAQ,GAAG,SAAS,KAC1B,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CA+G5C,CAAA"}
@@ -0,0 +1,17 @@
1
+ import type { AppendCondition } from './AppendCondition.js';
2
+ declare const AppendConditionFailed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
3
+ readonly _tag: "AppendConditionFailed";
4
+ } & Readonly<A>;
5
+ /**
6
+ * Raised by `append` when the append condition's `failIfEventsMatch` query
7
+ * matches at least one event after `condition.after`. Carries only the
8
+ * offending condition: a correct retry re-reads the store to its new head and
9
+ * rebuilds its condition from there, so no conflicting position is carried (it
10
+ * would already be stale by the time it was acted on).
11
+ */
12
+ export declare class AppendConditionFailed extends AppendConditionFailed_base<{
13
+ readonly condition: AppendCondition;
14
+ }> {
15
+ }
16
+ export {};
17
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;;;;AAExD;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,2BAEzC;IACA,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;CACpC,CAAC;CAAG"}
@@ -0,0 +1,76 @@
1
+ import { Effect, Layer } from 'effect';
2
+ import { DcbEventStore } from './DcbEventStore.js';
3
+ /**
4
+ * Options for the in-memory store factory.
5
+ *
6
+ * `beforeCommit` runs inside the append critical section, between the
7
+ * conflict-check and the commit. It defaults to `Effect.void` — a synchronous
8
+ * no-op — so the shipped critical section stays atomic by construction
9
+ * (`Ref.get` -> filter -> `Ref.set`, no suspension), which is the whole
10
+ * exactly-one-wins guarantee. It is a test seam: the concurrency contract test
11
+ * overrides it with `Effect.yieldNow()` to force a check/commit suspension, so
12
+ * the guarantee genuinely depends on `withPermits(1)` serialising across that
13
+ * boundary — without an injected suspension the test is vacuous (it passes even
14
+ * with the mutex removed). Real async backends (e.g. Postgres) have this
15
+ * boundary intrinsically; the in-memory store injects it only under test.
16
+ *
17
+ * `suppressWake` stops `append` publishing to the hub, so no wake ever reaches a
18
+ * live subscriber and the only path left to a delivery is the subscribe machine's
19
+ * periodic poll. It defaults to `false` — the shipped store always wakes — and it
20
+ * earns its place for the same reason `beforeCommit` does: the machine's stated
21
+ * guarantee is that the POLL and not the wake source is what makes delivery correct,
22
+ * and every ordinary subscribe test could be passing purely via the hub, which
23
+ * leaves that load-bearing claim asserted only in a comment. Suppressing the PUBLISH
24
+ * rather than the machine's `listen` keeps `subscribe`'s wiring identical to
25
+ * production's: what changes is that the wake never fires, which is exactly what a
26
+ * wake source that died silently does to a real engine.
27
+ *
28
+ * `catchUpPageSize` lowers the catch-up page size the subscribe machine runs this
29
+ * store's subscriptions at, defaulting to core's `DEFAULT_CATCH_UP_PAGE_SIZE` — and it
30
+ * earns its place the same way its siblings here do. At that default a catch-up drains
31
+ * in ONE page for any backlog a test would write, so the page loop's exit condition is
32
+ * taken on the FIRST page every time and `fetchPage`'s whole reason for existing is a
33
+ * claim about a boundary nothing reaches. Lowering the page brings the SECOND one
34
+ * within reach: a store that cannot be made to page cannot be asked whether it pages
35
+ * correctly.
36
+ *
37
+ * `pollInterval` lowers the ceiling on how long the subscribe machine waits for a
38
+ * wake before re-reading from its cursor anyway (milliseconds), defaulting to core's
39
+ * `DEFAULT_POLL_INTERVAL_MILLIS`. The oracle takes its subscribe tuning from exactly
40
+ * where the engines it judges take theirs, so a figure chosen here instead would be
41
+ * this store drifting from all of them by a factor nothing outside its own tests ever
42
+ * asked for.
43
+ *
44
+ * A seam rather than a shorter shipped default, and what that costs is MEASURED
45
+ * rather than assumed: at core's default, NO subscribe case in the shared contract
46
+ * suite reaches a poll timeout against this store. Those cases run on a real clock
47
+ * and finish in a few hundred milliseconds, and the hub wake carries every delivery
48
+ * they assert, so the poll — the half that carries delivery whenever no wake is
49
+ * heard — is never what delivered one. A case that wants the POLL to deliver here
50
+ * must therefore ask for an interval short enough to elapse inside it, and taking the
51
+ * wake away is what makes such a case prove anything. Asking is cheap: the machine
52
+ * treats the interval as a CEILING on a wait and never as a tick rate, so a wake
53
+ * still returns at once and only an IDLE subscription pays — one `fetch` per
54
+ * interval, which here is a binary search plus a bounded scan over an array.
55
+ */
56
+ export interface InMemoryStoreOptions {
57
+ readonly beforeCommit?: Effect.Effect<void>;
58
+ readonly suppressWake?: boolean;
59
+ readonly catchUpPageSize?: number;
60
+ readonly pollInterval?: number;
61
+ }
62
+ /** The in-memory `DcbEventStore` layer (production; no test seam). */
63
+ export declare const DcbEventStoreInMemory: Layer.Layer<DcbEventStore>;
64
+ /**
65
+ * Test-only in-memory layer factory: the same store as `DcbEventStoreInMemory`, but
66
+ * with `InMemoryStoreOptions`' seams open. Kept off the package's public surface
67
+ * (deliberately not re-exported from `index.ts`); production code always uses
68
+ * `DcbEventStoreInMemory`. Every seam exists to make a claim about this store
69
+ * FALSIFIABLE rather than merely commented — the store contract suite forces the
70
+ * check/commit interleaving the exactly-one-wins case needs, one core test takes the
71
+ * wake away and shortens the poll so that what delivers can only be the poll, and
72
+ * another lowers the catch-up page to reach the second-page boundary the shipped page
73
+ * size puts out of reach.
74
+ */
75
+ export declare const makeInMemoryTestLayer: (options: InMemoryStoreOptions) => Layer.Layer<DcbEventStore>;
76
+ //# sourceMappingURL=inMemory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAY,MAAM,EAAE,KAAK,EAAkC,MAAM,QAAQ,CAAA;AAIhF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAuL/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAuID,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAG5D,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,oBAAoB,KAC5B,KAAK,CAAC,KAAK,CAAC,aAAa,CAC+C,CAAA"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @kairos-es/core: DCB-first event-sourcing contract and in-memory store for
3
+ * Effect.
4
+ *
5
+ * Exposes the domain types (`DcbEvent`, `SequencedEvent`, `Position`, `Tag`,
6
+ * `EventType`), the selection language (`Query`, `QueryItem`, the pure match
7
+ * predicate + smart constructors, the servable grammar in both its dispositions —
8
+ * `assertServableQuery`'s throw and `servableQueryFault`'s total `string |
9
+ * undefined` — and the servable-shape dispatch every engine compiles its plans
10
+ * through), the `AppendCondition`, tagged errors, the
11
+ * `DcbEventStore` contract, `AppendIsOwned` and the refusal every engine's `append`
12
+ * inherits from it, the in-memory layer, the shared subscribe machine
13
+ * every engine's `subscribe` is built from (`SubscribeMachine.ts`), the pure
14
+ * projection primitives (`Projection`, `DecodedEvent`, `composeProjections` and the
15
+ * `unionQueries` its query merge is reachable through without a record), and
16
+ * `retryOnConflict`.
17
+ *
18
+ * Core keeps only the *pure* fold: projections fold an already-`DecodedEvent`
19
+ * (decoding needs `Schema` + a `Serializer`, which core must not depend on).
20
+ * The event-def builder and the decode-aware `buildDecisionModel` live in
21
+ * `@kairos-es/codec`.
22
+ *
23
+ * `retryOnConflict` is here because it is the recovery half of the append
24
+ * condition — the only consistency mechanism there is. It matches the
25
+ * `AppendConditionFailed` core defines and needs nothing beyond `effect`'s
26
+ * `Schedule`, which core already peers, so homing it downstream only made a
27
+ * write-only consumer depend on a package it uses nothing else of (ADR-0008).
28
+ */
29
+ export * from './AppendCondition.js';
30
+ export * from './AppendIsOwned.js';
31
+ export * from './ContractStore.js';
32
+ export * from './DcbEventStore.js';
33
+ export * from './Event.js';
34
+ export * from './errors.js';
35
+ export { DcbEventStoreInMemory } from './inMemory.js';
36
+ export * from './Projection.js';
37
+ export * from './Query.js';
38
+ export * from './retryOnConflict.js';
39
+ export * from './SubscribeMachine.js';
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AAIxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `retryOnConflict` — the decider-level retry for `AppendConditionFailed`
3
+ * (ADR-0008).
4
+ *
5
+ * `append` surfaces a conflict as a SIGNAL only: a tagged `AppendConditionFailed`
6
+ * flattened to the tripped condition, carrying no conflicting position,
7
+ * deliberately so that a retry cannot resume from a stale conflict point. A
8
+ * correct DCB retry must therefore re-derive its append-condition lower bound
9
+ * from a FRESH READ to the current head — which means re-running the whole
10
+ * read-decide-append cycle, not re-submitting the same events.
11
+ *
12
+ * That is why this is a combinator over a command and NOT a `Layer` decorating
13
+ * `DcbEventStore.append`. A decorator sees only the events and the condition; it
14
+ * cannot re-read the log and it cannot re-decide, so it is STRUCTURALLY incapable
15
+ * of a correct DCB retry — the only thing it could do is re-submit against a
16
+ * condition that is already known to be stale, which either fails again forever
17
+ * or, worse, succeeds against a boundary the decision was never made under. No
18
+ * retrying `Layer` is shipped, and if a production command-runner service ever
19
+ * emerges, a `Layer` over THAT (a whole cycle) is the shape to add.
20
+ *
21
+ * It lives HERE, in `@kairos-es/core`, because conflict retry is a WRITE-side
22
+ * concern and core is where the write side is defined: core declares the `append`
23
+ * contract, defines the `AppendConditionFailed` this combinator matches, and
24
+ * already peers `effect` for `Schedule`, so the move costs nothing and adds no
25
+ * dependency in either direction. It shipped from `@kairos-es/read` first on the
26
+ * argument that it added no peer surface there either — true, but an argument for
27
+ * where it MAY live, not where it SHOULD. What that home actually cost was a
28
+ * write-only consumer having to depend on the READ package to retry its own
29
+ * appends, for a combinator that touches no projection, no checkpoint and no view
30
+ * (ADR-0008, homing amended).
31
+ *
32
+ * Core stays pure with it here. `Schedule`'s driver reads the time through
33
+ * `Clock.currentTimeMillis` and sleeps through `Clock.sleep`, both resolved from
34
+ * the fibre's default services, so `TestClock` still governs every delay; the
35
+ * jitter draws from `Random.next`, a default service on the same footing as the
36
+ * clock rather than a host `Math.random`. No `node:*`, no wall-clock reading, no
37
+ * ungoverned RNG.
38
+ */
39
+ import { Effect, Schedule } from 'effect';
40
+ import type { AppendConditionFailed } from './errors.js';
41
+ /**
42
+ * Retry `command` while — and only while — it fails `AppendConditionFailed`.
43
+ *
44
+ * `command` must be the WHOLE read-decide-append cycle (read a decision model to
45
+ * the current head, decide, append under that read's condition), because each
46
+ * retry's correctness comes from the re-read: the fresh read re-derives the
47
+ * append condition's `after` from the new head, so the retried append is judged
48
+ * against the boundary the retried decision was actually made under. Wrapping only
49
+ * the `append` re-submits a stale condition and is wrong.
50
+ *
51
+ * A non-conflict `E` is preserved on the error channel and escapes IMMEDIATELY
52
+ * with zero retries: the `while` predicate is false for it, so a domain rejection
53
+ * ("course is full") never pays a backoff and never retries into a decision the
54
+ * log has already refused. `A`, the non-conflict `E`, and `R` all pass through
55
+ * untouched.
56
+ *
57
+ * The predicate matches on the `_tag` rather than `instanceof`. The tag is the
58
+ * error's identity across module realms, and a published package ships dual
59
+ * ESM/CJS, so an `instanceof` check can see two distinct classes for the same
60
+ * error when a consumer's graph loads both builds — a conflict would then escape
61
+ * unretried and silently.
62
+ */
63
+ export declare const retryOnConflict: <A, E, R>(command: Effect.Effect<A, AppendConditionFailed | E, R>, schedule?: Schedule.Schedule<unknown, AppendConditionFailed | E, never, R>) => Effect.Effect<A, AppendConditionFailed | E, R>;
64
+ //# sourceMappingURL=retryOnConflict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retryOnConflict.d.ts","sourceRoot":"","sources":["../../src/retryOnConflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAE,MAAM,EAAa,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAqBrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACrC,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,EAAE,CAAC,CAAC,EACvD,WAAU,QAAQ,CAAC,QAAQ,CACzB,OAAO,EACP,qBAAqB,GAAG,CAAC,EACzB,KAAK,EACL,CAAC,CACgB,KAClB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,EAAE,CAAC,CAI5C,CAAA"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Deterministic test-support utilities, shared across every kairos-es suite via
3
+ * the published `@kairos-es/core/testing` subpath.
4
+ *
5
+ * WHY a published subpath rather than a test-only package: three suites (core's
6
+ * own tests, the store contract suite, and the codec's `UuidTest` layer) each
7
+ * carried a byte-identical copy of the same counter-based uuid scheme and the
8
+ * `EPOCH` domain time. Copies drift. The one place every consumer already
9
+ * depends on is `@kairos-es/core`, so the single source of truth lives here as a
10
+ * *published* subpath — never a runtime dependency from a published package onto
11
+ * an unpublished/test-only one, which would create a packaging cycle.
12
+ *
13
+ * Everything here is PURE and DETERMINISTIC: no RNG, no wall-clock. That is the
14
+ * whole point — suites must produce the same bytes and positions on every run,
15
+ * so `data`/`uuid`/`occurredAt` never introduce nondeterminism into assertions.
16
+ */
17
+ import { DateTime } from 'effect';
18
+ /**
19
+ * A fixed domain time (`DateTime` at the Unix epoch) for test events.
20
+ *
21
+ * Ordering in the log is by `position` alone, so an event's `occurredAt` is
22
+ * informational; pinning it to the epoch keeps serialised bytes and comparisons
23
+ * stable across runs. Built via `DateTime.makeUnsafe(0)` (the value is a
24
+ * compile-time constant, not read from the host clock).
25
+ */
26
+ export declare const EPOCH: DateTime.Utc;
27
+ /**
28
+ * Build a fresh, deterministic uuid generator: a zero-based counter producing
29
+ * RFC-4122 v4-shaped ids `00000000-0000-4000-8000-<counter as 12 hex digits>`,
30
+ * starting at `...000000000000` and incrementing on each call.
31
+ *
32
+ * A *factory* (not a shared singleton) so each layer/suite instance owns its own
33
+ * counter and the sequence is repeatable in isolation — two independent suites
34
+ * do not interfere, yet each is stable on its own. No RNG: the ids are
35
+ * comparable and predictable, which is what assertions need, while still being
36
+ * distinct enough to tell events apart.
37
+ */
38
+ export declare const makeSequentialUuid: () => (() => string);
39
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,cAAyB,CAAA;AAE3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,QAAO,CAAC,MAAM,MAAM,CAOlD,CAAA"}
@@ -12,8 +12,6 @@ import { Position } from "./Event.js";
12
12
  import { Query } from "./Query.js";
13
13
  export const AppendCondition = /*#__PURE__*/Schema.Struct({
14
14
  failIfEventsMatch: Query,
15
- after: /*#__PURE__*/Schema.optionalWith(Position, {
16
- exact: true
17
- })
15
+ after: /*#__PURE__*/Schema.optionalKey(Position)
18
16
  });
19
17
  //# sourceMappingURL=AppendCondition.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppendCondition.js","names":["Schema","Position","Query","AppendCondition","Struct","failIfEventsMatch","after","optionalWith","exact"],"sources":["../../src/AppendCondition.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;AASA,SAASA,MAAM,QAAQ,QAAQ;AAC/B,SAASC,QAAQ,QAAQ,YAAS;AAClC,SAASC,KAAK,QAAQ,YAAS;AAE/B,OAAO,MAAMC,eAAe,gBAAGH,MAAM,CAACI,MAAM,CAAC;EAC3CC,iBAAiB,EAAEH,KAAK;EACxBI,KAAK,eAAEN,MAAM,CAACO,YAAY,CAACN,QAAQ,EAAE;IAAEO,KAAK,EAAE;EAAI,CAAE;CACrD,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"AppendCondition.js","names":["Schema","Position","Query","AppendCondition","Struct","failIfEventsMatch","after","optionalKey"],"sources":["../../src/AppendCondition.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;AASA,SAASA,MAAM,QAAQ,QAAQ;AAC/B,SAASC,QAAQ,QAAQ,YAAS;AAClC,SAASC,KAAK,QAAQ,YAAS;AAE/B,OAAO,MAAMC,eAAe,gBAAGH,MAAM,CAACI,MAAM,CAAC;EAC3CC,iBAAiB,EAAEH,KAAK;EACxBI,KAAK,eAAEN,MAAM,CAACO,WAAW,CAACN,QAAQ;CACnC,CAAC","ignoreList":[]}
@@ -30,15 +30,15 @@
30
30
  *
31
31
  * ## Present means refused, so a sanctioned append REMOVES the marker
32
32
  *
33
- * The check reads the tag through `Effect.serviceOption`, so its ABSENCE is the
33
+ * The check reads the service through `Effect.serviceOption`, so its ABSENCE is the
34
34
  * normal case and costs the `append` signature nothing — `DcbEventStore.append`
35
35
  * keeps `R = never` and no caller provides anything. Presence is what refuses.
36
36
  *
37
37
  * That shape decides how the two legitimate appends inside a bracket get through,
38
38
  * and there is only one route: run with the marker STRIPPED from the context
39
- * (`Effect.mapInputContext` over `Context.omit(AppendIsOwned)`). The bracket's own
39
+ * (`Effect.updateContext` over `Context.omit(AppendIsOwned)`). The bracket's own
40
40
  * closing append is issued outside the scope it provided the marker in, and the
41
- * codec's escape hatch strips it explicitly. Neither needs a second tag, a
41
+ * codec's escape hatch strips it explicitly. Neither needs a second service, a
42
42
  * sanctioning flag or a mutable cell, and there is no way to be exempt by
43
43
  * accident: an append is either lexically outside the marked region or it says in
44
44
  * as many words that it is removing the marker.
@@ -49,10 +49,10 @@ import { Context, Effect } from 'effect';
49
49
  * owns it.
50
50
  *
51
51
  * `by` is a human-readable owner name and exists for the defect message alone —
52
- * the refusal is decided by the tag's PRESENCE, never by this value, so nothing
52
+ * the refusal is decided by the service's PRESENCE, never by this value, so nothing
53
53
  * branches on it and no caller need agree on a vocabulary for it.
54
54
  */
55
- export class AppendIsOwned extends /*#__PURE__*/Context.Tag('@kairos-es/core/AppendIsOwned')() {}
55
+ export class AppendIsOwned extends /*#__PURE__*/Context.Service()('@kairos-es/core/AppendIsOwned') {}
56
56
  /**
57
57
  * Die if an append is already owned here; do nothing otherwise.
58
58
  *
@@ -65,7 +65,7 @@ export class AppendIsOwned extends /*#__PURE__*/Context.Tag('@kairos-es/core/App
65
65
  * its escape hatch for a deliberate second append — which is what makes a defect
66
66
  * affordable here.
67
67
  *
68
- * `Effect.serviceOption` is what keeps this free: it reads the tag if present and
68
+ * `Effect.serviceOption` is what keeps this free: it reads the service if present and
69
69
  * adds NOTHING to the requirement channel, so every existing append is unchanged
70
70
  * and unprovisioned.
71
71
  */
@@ -1 +1 @@
1
- {"version":3,"file":"AppendIsOwned.js","names":["Context","Effect","AppendIsOwned","Tag","assertAppendNotOwned","flatMap","serviceOption","owner","_tag","void","die","Error","value","by"],"sources":["../../src/AppendIsOwned.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAASA,OAAO,EAAEC,MAAM,QAAQ,QAAQ;AAExC;;;;;;;;AAQA,OAAM,MAAOC,aAAc,sBAAQF,OAAO,CAACG,GAAG,CAAC,+BAA+B,CAAC,EAG5E;AAEH;;;;;;;;;;;;;;;;AAgBA,OAAO,MAAMC,oBAAoB,gBAAwBH,MAAM,CAACI,OAAO,cACrEJ,MAAM,CAACK,aAAa,CAACJ,aAAa,CAAC,EAClCK,KAAK,IACJA,KAAK,CAACC,IAAI,KAAK,MAAM,GACjBP,MAAM,CAACQ,IAAI,GACXR,MAAM,CAACS,GAAG,CACR,IAAIC,KAAK,CACP,kEAAkEJ,KAAK,CAACK,KAAK,CAACC,EAAE,MAAM,GACpF,4EAA4E,GAC5E,0EAA0E,GAC1E,4EAA4E,GAC5E,0EAA0E,GAC1E,sEAAsE,CACzE,CACF,CACR","ignoreList":[]}
1
+ {"version":3,"file":"AppendIsOwned.js","names":["Context","Effect","AppendIsOwned","Service","assertAppendNotOwned","flatMap","serviceOption","owner","_tag","void","die","Error","value","by"],"sources":["../../src/AppendIsOwned.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAASA,OAAO,EAAEC,MAAM,QAAQ,QAAQ;AAExC;;;;;;;;AAQA,OAAM,MAAOC,aAAc,sBAAQF,OAAO,CAACG,OAAO,EAG/C,CAAC,+BAA+B,CAAC;AAEpC;;;;;;;;;;;;;;;;AAgBA,OAAO,MAAMC,oBAAoB,gBAAwBH,MAAM,CAACI,OAAO,cACrEJ,MAAM,CAACK,aAAa,CAACJ,aAAa,CAAC,EAClCK,KAAK,IACJA,KAAK,CAACC,IAAI,KAAK,MAAM,GACjBP,MAAM,CAACQ,IAAI,GACXR,MAAM,CAACS,GAAG,CACR,IAAIC,KAAK,CACP,kEAAkEJ,KAAK,CAACK,KAAK,CAACC,EAAE,MAAM,GACpF,4EAA4E,GAC5E,0EAA0E,GAC1E,4EAA4E,GAC5E,0EAA0E,GAC1E,sEAAsE,CACzE,CACF,CACR","ignoreList":[]}
@@ -22,7 +22,7 @@ export const makeContractStore = raw => ({
22
22
  append: (events, condition) =>
23
23
  // The ownership refusal runs FIRST and sits OUTSIDE the suspend, because it is
24
24
  // the only one of append's checks that is EFFECTFUL: it consults a
25
- // `Context.Tag` rather than inspecting the arguments. `Effect.flatMap` over an
25
+ // `Context.Service` rather than inspecting the arguments. `Effect.flatMap` over an
26
26
  // already-lazy effect defers it exactly as the suspend defers the throws below,
27
27
  // so nothing runs at the call site either way; what it buys is that the
28
28
  // "you are inside a bracket" defect arrives before any argument is judged, which
@@ -77,7 +77,7 @@ export const makeContractStore = raw => ({
77
77
  // engine's channel simply answers false and dies as it should. It also
78
78
  // narrows the raw `AppendConditionFailed | E` to the contract's single
79
79
  // error, which is what keeps `append` at `E = AppendConditionFailed`.
80
- return Effect.catchAll(raw.append(events, condition), error => Predicate.isTagged('AppendConditionFailed')(error) ? Effect.fail(error) : Effect.die(error));
80
+ return Effect.catch(raw.append(events, condition), error => Predicate.isTagged('AppendConditionFailed')(error) ? Effect.fail(error) : Effect.die(error));
81
81
  })),
82
82
  subscribe: (query, after) =>
83
83
  // `Effect.sync` runs when the stream is executed; a throw from
@@ -1 +1 @@
1
- {"version":3,"file":"ContractStore.js","names":["Effect","Predicate","Schema","Stream","AppendCondition","assertAppendNotOwned","ReadLimit","assertServableQuery","decodeReadLimit","decodeUnknownSync","makeContractStore","raw","read","query","options","suspend","limit","undefined","orDie","append","events","condition","flatMap","length","Error","unknown","Object","keys","find","key","hasOwn","fields","JSON","stringify","failIfEventsMatch","catchAll","error","isTagged","fail","die","subscribe","after","unwrap","sync"],"sources":["../../src/ContractStore.ts"],"sourcesContent":[null],"mappings":"AAqCA,SAASA,MAAM,EAAEC,SAAS,EAAEC,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AAC1D,SAASC,eAAe,QAAQ,sBAAmB;AACnD,SAASC,oBAAoB,QAAQ,oBAAiB;AAGtD,SAASC,SAAS,QAAQ,YAAS;AAEnC,SAASC,mBAAmB,QAAoB,YAAS;AAEzD;AACA,MAAMC,eAAe,gBAAGN,MAAM,CAACO,iBAAiB,CAACH,SAAS,CAAC;AAwB3D;;;;AAIA,OAAO,MAAMI,iBAAiB,GAC5BC,GAAwB,KACG;EAC3BC,IAAI,EAAEA,CAACC,KAAK,EAAEC,OAAO,KACnBd,MAAM,CAACe,OAAO,CAAC,MAAK;IAClB;IACAR,mBAAmB,CAACM,KAAK,CAAC;IAC1B,IAAIC,OAAO,EAAEE,KAAK,KAAKC,SAAS,EAAE;MAChCT,eAAe,CAACM,OAAO,CAACE,KAAK,CAAC;IAChC;IACA;IACA,OAAOhB,MAAM,CAACkB,KAAK,CAACP,GAAG,CAACC,IAAI,CAACC,KAAK,EAAEC,OAAO,CAAC,CAAC;EAC/C,CAAC,CAAC;EAEJK,MAAM,EAAEA,CAACC,MAAM,EAAEC,SAAS;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACArB,MAAM,CAACsB,OAAO,CAACjB,oBAAoB,EAAE,MACnCL,MAAM,CAACe,OAAO,CAAC,MAAK;IAClB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIK,MAAM,CAACG,MAAM,KAAK,CAAC,EAAE;MACvB,MAAM,IAAIC,KAAK,CACb,uEAAuE,GACrE,oEAAoE,GACpE,kEAAkE,CACrE;IACH;IACA;IACA;IACA;IACA;IACA;IACA,IAAIH,SAAS,KAAKJ,SAAS,EAAE;MAC3B,MAAMQ,OAAO,GAAGC,MAAM,CAACC,IAAI,CAACN,SAAS,CAAC,CAACO,IAAI,CACxCC,GAAG,IAAK,CAACH,MAAM,CAACI,MAAM,CAAC1B,eAAe,CAAC2B,MAAM,EAAEF,GAAG,CAAC,CACrD;MACD,IAAIJ,OAAO,KAAKR,SAAS,EAAE;QACzB,MAAM,IAAIO,KAAK,CACb,kEAAkEQ,IAAI,CAACC,SAAS,CAACR,OAAO,CAAC,IAAI,GAC3F,mEAAmE,GACnE,yEAAyE,CAC5E;MACH;MACAlB,mBAAmB,CAACc,SAAS,CAACa,iBAAiB,CAAC;IAClD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,OAAOlC,MAAM,CAACmC,QAAQ,CAACxB,GAAG,CAACQ,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,EAAGe,KAAK,IAC1DnC,SAAS,CAACoC,QAAQ,CAAC,uBAAuB,CAAC,CAACD,KAAK,CAAC,GAC9CpC,MAAM,CAACsC,IAAI,CAACF,KAAK,CAAC,GAClBpC,MAAM,CAACuC,GAAG,CAACH,KAAK,CAAC,CACtB;EACH,CAAC,CAAC,CACH;EAEHI,SAAS,EAAEA,CAAC3B,KAAK,EAAE4B,KAAK;EACtB;EACA;EACA;EACA;EACAtC,MAAM,CAACuC,MAAM,CACX1C,MAAM,CAAC2C,IAAI,CAAC,MAAK;IACfpC,mBAAmB,CAACM,KAAK,CAAC;IAC1B,OAAOV,MAAM,CAACe,KAAK,CAACP,GAAG,CAAC6B,SAAS,CAAC3B,KAAK,EAAE4B,KAAK,CAAC,CAAC;EAClD,CAAC,CAAC;CAEP,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"ContractStore.js","names":["Effect","Predicate","Schema","Stream","AppendCondition","assertAppendNotOwned","ReadLimit","assertServableQuery","decodeReadLimit","decodeUnknownSync","makeContractStore","raw","read","query","options","suspend","limit","undefined","orDie","append","events","condition","flatMap","length","Error","unknown","Object","keys","find","key","hasOwn","fields","JSON","stringify","failIfEventsMatch","catch","error","isTagged","fail","die","subscribe","after","unwrap","sync"],"sources":["../../src/ContractStore.ts"],"sourcesContent":[null],"mappings":"AAsCA,SAASA,MAAM,EAAEC,SAAS,EAAEC,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AAC1D,SAASC,eAAe,QAAQ,sBAAmB;AACnD,SAASC,oBAAoB,QAAQ,oBAAiB;AAGtD,SAASC,SAAS,QAAQ,YAAS;AAEnC,SAASC,mBAAmB,QAAoB,YAAS;AAEzD;AACA,MAAMC,eAAe,gBAAGN,MAAM,CAACO,iBAAiB,CAACH,SAAS,CAAC;AAwB3D;;;;AAIA,OAAO,MAAMI,iBAAiB,GAC5BC,GAAwB,KACM;EAC9BC,IAAI,EAAEA,CAACC,KAAK,EAAEC,OAAO,KACnBd,MAAM,CAACe,OAAO,CAAC,MAAK;IAClB;IACAR,mBAAmB,CAACM,KAAK,CAAC;IAC1B,IAAIC,OAAO,EAAEE,KAAK,KAAKC,SAAS,EAAE;MAChCT,eAAe,CAACM,OAAO,CAACE,KAAK,CAAC;IAChC;IACA;IACA,OAAOhB,MAAM,CAACkB,KAAK,CAACP,GAAG,CAACC,IAAI,CAACC,KAAK,EAAEC,OAAO,CAAC,CAAC;EAC/C,CAAC,CAAC;EAEJK,MAAM,EAAEA,CAACC,MAAM,EAAEC,SAAS;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACArB,MAAM,CAACsB,OAAO,CAACjB,oBAAoB,EAAE,MACnCL,MAAM,CAACe,OAAO,CAAC,MAAK;IAClB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIK,MAAM,CAACG,MAAM,KAAK,CAAC,EAAE;MACvB,MAAM,IAAIC,KAAK,CACb,uEAAuE,GACrE,oEAAoE,GACpE,kEAAkE,CACrE;IACH;IACA;IACA;IACA;IACA;IACA;IACA,IAAIH,SAAS,KAAKJ,SAAS,EAAE;MAC3B,MAAMQ,OAAO,GAAGC,MAAM,CAACC,IAAI,CAACN,SAAS,CAAC,CAACO,IAAI,CACxCC,GAAG,IAAK,CAACH,MAAM,CAACI,MAAM,CAAC1B,eAAe,CAAC2B,MAAM,EAAEF,GAAG,CAAC,CACrD;MACD,IAAIJ,OAAO,KAAKR,SAAS,EAAE;QACzB,MAAM,IAAIO,KAAK,CACb,kEAAkEQ,IAAI,CAACC,SAAS,CAACR,OAAO,CAAC,IAAI,GAC3F,mEAAmE,GACnE,yEAAyE,CAC5E;MACH;MACAlB,mBAAmB,CAACc,SAAS,CAACa,iBAAiB,CAAC;IAClD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,OAAOlC,MAAM,CAACmC,KAAK,CAACxB,GAAG,CAACQ,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,EAAGe,KAAK,IACvDnC,SAAS,CAACoC,QAAQ,CAAC,uBAAuB,CAAC,CAACD,KAAK,CAAC,GAC9CpC,MAAM,CAACsC,IAAI,CAACF,KAAK,CAAC,GAClBpC,MAAM,CAACuC,GAAG,CAACH,KAAK,CAAC,CACtB;EACH,CAAC,CAAC,CACH;EAEHI,SAAS,EAAEA,CAAC3B,KAAK,EAAE4B,KAAK;EACtB;EACA;EACA;EACA;EACAtC,MAAM,CAACuC,MAAM,CACX1C,MAAM,CAAC2C,IAAI,CAAC,MAAK;IACfpC,mBAAmB,CAACM,KAAK,CAAC;IAC1B,OAAOV,MAAM,CAACe,KAAK,CAACP,GAAG,CAAC6B,SAAS,CAAC3B,KAAK,EAAE4B,KAAK,CAAC,CAAC;EAClD,CAAC,CAAC;CAEP,CAAC","ignoreList":[]}
@@ -1,3 +1,3 @@
1
1
  import { Context } from 'effect';
2
- export class DcbEventStore extends /*#__PURE__*/Context.Tag('@kairos-es/core/DcbEventStore')() {}
2
+ export class DcbEventStore extends /*#__PURE__*/Context.Service()('@kairos-es/core/DcbEventStore') {}
3
3
  //# sourceMappingURL=DcbEventStore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DcbEventStore.js","names":["Context","DcbEventStore","Tag"],"sources":["../../src/DcbEventStore.ts"],"sourcesContent":[null],"mappings":"AAiBA,SAASA,OAAO,QAAQ,QAAQ;AA2BhC,OAAM,MAAOC,aAAc,sBAAQD,OAAO,CAACE,GAAG,CAAC,+BAA+B,CAAC,EAgC5E","ignoreList":[]}
1
+ {"version":3,"file":"DcbEventStore.js","names":["Context","DcbEventStore","Service"],"sources":["../../src/DcbEventStore.ts"],"sourcesContent":[null],"mappings":"AAiBA,SAASA,OAAO,QAAQ,QAAQ;AA2BhC,OAAM,MAAOC,aAAc,sBAAQD,OAAO,CAACE,OAAO,EAgC/C,CAAC,+BAA+B,CAAC","ignoreList":[]}