@kairos-es/read 0.0.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 (82) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +538 -0
  3. package/dist/cjs/EventLogDurability.js +184 -0
  4. package/dist/cjs/EventLogDurability.js.map +1 -0
  5. package/dist/cjs/ProjectionRunner.js +478 -0
  6. package/dist/cjs/ProjectionRunner.js.map +1 -0
  7. package/dist/cjs/ProjectionStore.js +233 -0
  8. package/dist/cjs/ProjectionStore.js.map +1 -0
  9. package/dist/cjs/foldIntoRef.js +36 -0
  10. package/dist/cjs/foldIntoRef.js.map +1 -0
  11. package/dist/cjs/inMemoryProjectionStore.js +138 -0
  12. package/dist/cjs/inMemoryProjectionStore.js.map +1 -0
  13. package/dist/cjs/index.js +128 -0
  14. package/dist/cjs/index.js.map +1 -0
  15. package/dist/cjs/projectionWiringFault.js +532 -0
  16. package/dist/cjs/projectionWiringFault.js.map +1 -0
  17. package/dist/cjs/runProjection.js +117 -0
  18. package/dist/cjs/runProjection.js.map +1 -0
  19. package/dist/cjs/runProjections.js +144 -0
  20. package/dist/cjs/runProjections.js.map +1 -0
  21. package/dist/cjs/superviseOnProgress.js +580 -0
  22. package/dist/cjs/superviseOnProgress.js.map +1 -0
  23. package/dist/cjs/testing.js +143 -0
  24. package/dist/cjs/testing.js.map +1 -0
  25. package/dist/dts/EventLogDurability.d.ts +182 -0
  26. package/dist/dts/EventLogDurability.d.ts.map +1 -0
  27. package/dist/dts/ProjectionRunner.d.ts +557 -0
  28. package/dist/dts/ProjectionRunner.d.ts.map +1 -0
  29. package/dist/dts/ProjectionStore.d.ts +475 -0
  30. package/dist/dts/ProjectionStore.d.ts.map +1 -0
  31. package/dist/dts/foldIntoRef.d.ts +39 -0
  32. package/dist/dts/foldIntoRef.d.ts.map +1 -0
  33. package/dist/dts/inMemoryProjectionStore.d.ts +11 -0
  34. package/dist/dts/inMemoryProjectionStore.d.ts.map +1 -0
  35. package/dist/dts/index.d.ts +185 -0
  36. package/dist/dts/index.d.ts.map +1 -0
  37. package/dist/dts/projectionWiringFault.d.ts +260 -0
  38. package/dist/dts/projectionWiringFault.d.ts.map +1 -0
  39. package/dist/dts/runProjection.d.ts +185 -0
  40. package/dist/dts/runProjection.d.ts.map +1 -0
  41. package/dist/dts/runProjections.d.ts +480 -0
  42. package/dist/dts/runProjections.d.ts.map +1 -0
  43. package/dist/dts/superviseOnProgress.d.ts +587 -0
  44. package/dist/dts/superviseOnProgress.d.ts.map +1 -0
  45. package/dist/dts/testing.d.ts +207 -0
  46. package/dist/dts/testing.d.ts.map +1 -0
  47. package/dist/esm/EventLogDurability.js +175 -0
  48. package/dist/esm/EventLogDurability.js.map +1 -0
  49. package/dist/esm/ProjectionRunner.js +468 -0
  50. package/dist/esm/ProjectionRunner.js.map +1 -0
  51. package/dist/esm/ProjectionStore.js +223 -0
  52. package/dist/esm/ProjectionStore.js.map +1 -0
  53. package/dist/esm/foldIntoRef.js +29 -0
  54. package/dist/esm/foldIntoRef.js.map +1 -0
  55. package/dist/esm/inMemoryProjectionStore.js +131 -0
  56. package/dist/esm/inMemoryProjectionStore.js.map +1 -0
  57. package/dist/esm/index.js +185 -0
  58. package/dist/esm/index.js.map +1 -0
  59. package/dist/esm/package.json +4 -0
  60. package/dist/esm/projectionWiringFault.js +524 -0
  61. package/dist/esm/projectionWiringFault.js.map +1 -0
  62. package/dist/esm/runProjection.js +109 -0
  63. package/dist/esm/runProjection.js.map +1 -0
  64. package/dist/esm/runProjections.js +137 -0
  65. package/dist/esm/runProjections.js.map +1 -0
  66. package/dist/esm/superviseOnProgress.js +571 -0
  67. package/dist/esm/superviseOnProgress.js.map +1 -0
  68. package/dist/esm/testing.js +133 -0
  69. package/dist/esm/testing.js.map +1 -0
  70. package/package.json +41 -0
  71. package/src/EventLogDurability.ts +201 -0
  72. package/src/ProjectionRunner.ts +923 -0
  73. package/src/ProjectionStore.ts +528 -0
  74. package/src/foldIntoRef.ts +63 -0
  75. package/src/inMemoryProjectionStore.ts +163 -0
  76. package/src/index.ts +218 -0
  77. package/src/projectionWiringFault.ts +694 -0
  78. package/src/runProjection.ts +270 -0
  79. package/src/runProjections.ts +623 -0
  80. package/src/superviseOnProgress.ts +897 -0
  81. package/src/testing.ts +290 -0
  82. package/testing/package.json +6 -0
@@ -0,0 +1,223 @@
1
+ import { Data, Effect, Schema } from 'effect';
2
+ /**
3
+ * The name of a read model, as a branded non-empty string.
4
+ *
5
+ * Branded rather than a bare `string` for the same reason every other kairos-es
6
+ * identifier is (`Position`, `Tag`, `EventType`): the projection name and the
7
+ * partition name are both non-empty strings with identical shape, so an
8
+ * unbranded pair is trivially transposable at a call site and the mistake would
9
+ * surface only as a projection silently reading somebody else's checkpoint.
10
+ * Construct with `ProjectionId.make(...)`.
11
+ */
12
+ export const ProjectionId = /*#__PURE__*/Schema.String.pipe(/*#__PURE__*/Schema.nonEmptyString(), /*#__PURE__*/Schema.brand('ProjectionId'));
13
+ /**
14
+ * The partition of a read model's checkpoint, as a branded non-empty string.
15
+ *
16
+ * `DEFAULT_PARTITION` is the single value the RUNNER uses, because stream
17
+ * sharding is deliberately not offered: DCB events carry a tag matrix, so two
18
+ * shards can own rows for the same tag while each holds its own checkpoint —
19
+ * both guarded compare-and-sets then succeed and the lost update goes undetected
20
+ * (the guard arbitrates cursor progress, not row ownership). Nothing on this port
21
+ * restricts the value, and a `ProjectionStore` must keep every distinct
22
+ * `(projection, partition)` pair independent — the shared contract suite commits
23
+ * to a second partition precisely to prove that. The dimension is in the KEY so
24
+ * that a restricted, opt-in sharding capability — for read models that provably
25
+ * need no cross-tag ordering and whose every row is owned by one partition value
26
+ * — could be added later with no migration of the checkpoint's shape or storage.
27
+ */
28
+ export const PartitionId = /*#__PURE__*/Schema.String.pipe(/*#__PURE__*/Schema.nonEmptyString(), /*#__PURE__*/Schema.brand('PartitionId'));
29
+ /** The partition value every runner uses while sharding is cut (ADR-0007). */
30
+ export const DEFAULT_PARTITION = /*#__PURE__*/PartitionId.make('default');
31
+ /**
32
+ * Build a `CheckpointKey`, defaulting the partition — the constructor nearly
33
+ * every call site wants, so that the sharding dimension stays in the key shape
34
+ * without appearing in ordinary wiring code.
35
+ */
36
+ export const checkpointKey = (projection, partition = DEFAULT_PARTITION) => ({
37
+ projection,
38
+ partition
39
+ });
40
+ /**
41
+ * An infrastructure fault from a view store — a connection dropped, a statement
42
+ * failed, the checkpoint table is missing.
43
+ *
44
+ * This is a value on the ERROR channel rather than a defect, which inverts the
45
+ * store contract's rule (`DcbEventStore` dies on infrastructure faults, ADR-0002)
46
+ * and does so on purpose: the read side has a supervisor above it, and the
47
+ * supervisor's whole job is to tell "retry the subscription with backoff" from
48
+ * "give up and report a stalled projection". A defect carries no such
49
+ * distinction. `cause` is exact-optional — only set when there is one.
50
+ */
51
+ export class ProjectionStoreError extends /*#__PURE__*/Data.TaggedError('ProjectionStoreError') {
52
+ /**
53
+ * `reason` again, as the `Error` field that says why — so the fault renders
54
+ * ITSELF.
55
+ *
56
+ * `Data.TaggedError` sets `name` to the tag and leaves `message` empty, so
57
+ * without this a `ProjectionStoreError` stringifies to `'ProjectionStoreError'`
58
+ * and the sentence explaining the fault reaches a log line only where something
59
+ * OUTSIDE the class knows to go and fetch `reason`. With it,
60
+ * `Error.prototype.toString` yields `'ProjectionStoreError: <reason>'`, and
61
+ * `String(fault)`, `Cause.pretty` (which reads `message` off anything `instanceof
62
+ * Error`) and the projection supervisor's `reason` annotation all take it from the
63
+ * one place it is declared. That matters most for exactly the audience this error
64
+ * exists for: the supervisor above the read side, whose log line is what an
65
+ * operator sees when a view store starts refusing to answer.
66
+ *
67
+ * ## What filling `message` reaches — and what it deliberately leaves alone
68
+ *
69
+ * Not the supervisor's two log lines: `message` is the field `Cause.pretty` reads
70
+ * off anything `instanceof Error`, so this changes how a `ProjectionStoreError`
71
+ * prints EVERYWHERE it is reported — a failed fibre's `cause` annotation on all
72
+ * three shipped loggers, `Effect.logError(message, cause)`, an unhandled
73
+ * failure's report, and any reporter or test runner that reads `.message`. That
74
+ * blast radius is the point of doing it on the class instead of at one call site:
75
+ * one declaration improves every reader at once, and none of them has to know
76
+ * this class's field names.
77
+ *
78
+ * What it does NOT change is the STRUCTURED shape. `Data.Error` overrides
79
+ * `YieldableError`'s `toJSON` with `{ ...plainArgs, ...this }` — own enumerables
80
+ * plus the constructor args, and a prototype accessor is neither — so
81
+ * `JSON.stringify(fault)` and an `Effect.logError(fault)` that JSON-encodes its
82
+ * object message emit exactly the `reason`/`_tag` they always did. The humane
83
+ * rendering gains a sentence; the machine-parsed one is untouched.
84
+ *
85
+ * A GETTER rather than a second field, so `reason` stays the single declaration
86
+ * and the two cannot disagree. Safe as a prototype accessor because
87
+ * `Data.TaggedError`'s constructor is `super(args?.message, …)` followed by
88
+ * `Object.assign(this, args)` (verified against `effect@3.22`'s `Data.ts`) and
89
+ * `message` is not among the fields above, so nothing shadows it with an own
90
+ * property.
91
+ *
92
+ * Both mechanics — that accessor safety and the `toJSON` shape above it — are
93
+ * argued HERE and nowhere else, for every read-side fault that fills the field:
94
+ * `CheckpointSuperseded` below, `PipelineDied` in `ProjectionRunner.ts` and
95
+ * `ProjectionStalled` in `superviseOnProgress.ts` each point back rather than
96
+ * re-derive, and none of the four declares a `message` field for that
97
+ * `Object.assign` to shadow its accessor with. One version pin to re-verify on an
98
+ * `effect` bump, not four.
99
+ */
100
+ get message() {
101
+ return this.reason;
102
+ }
103
+ }
104
+ /**
105
+ * The guarded compare-and-set lost: the stored checkpoint no longer equals
106
+ * `expected`, so somebody else advanced this key.
107
+ *
108
+ * A tagged ERROR, never a returned outcome value. A returned
109
+ * `Advanced | Superseded` only rolls the view writes back if the caller remembers
110
+ * to convert it into a failure, and forgetting is silent corruption — the view
111
+ * commits while the checkpoint stays put, and the next pass double-applies the
112
+ * batch. On the error channel the abort is automatic and cannot be ignored.
113
+ *
114
+ * It carries the key and the expectation but deliberately NOT the stored
115
+ * position, mirroring `AppendConditionFailed` (ADR-0002) for the same reason: the
116
+ * recovery path re-reads the checkpoint, so a carried position would already be
117
+ * stale by the time anything acted on it, and carrying one would invite exactly
118
+ * the resume-from-the-conflict-point bug that omitting it prevents.
119
+ *
120
+ * Like its sibling `ProjectionStoreError` it fills `message`, from the expectation
121
+ * it already carries. The getter below says what the field reaches, and also
122
+ * records the reasoning it REPLACED — this class once left `message` empty on a
123
+ * ground that could not be true, and that is worth being able to recognise again.
124
+ */
125
+ export class CheckpointSuperseded extends /*#__PURE__*/Data.TaggedError('CheckpointSuperseded') {
126
+ /**
127
+ * The sentence this class's own first paragraph writes, plus the expectation the
128
+ * guard turned on — as the `Error` field that says why, so the fault renders
129
+ * ITSELF.
130
+ *
131
+ * `Data.TaggedError` sets `name` to the tag and leaves `message` empty, and
132
+ * `Cause.pretty` substitutes its own `'An error has occurred'` for an empty one,
133
+ * so unfilled this fault printed through a `Cause` reads
134
+ * `'CheckpointSuperseded: An error has occurred'` — a line naming neither a guard,
135
+ * nor a checkpoint, nor an expectation. Filled, `Error.prototype.toString` yields
136
+ * the tag followed by the sentence, everywhere the fault is reported. How far that
137
+ * reaches and what it deliberately leaves alone is set out on
138
+ * `ProjectionStoreError` above; the argument is identical for every fault that
139
+ * fills the field, so it is made once, on the one an operator meets most.
140
+ *
141
+ * ## Why this field was once left EMPTY, and why that reason was wrong
142
+ *
143
+ * Recorded rather than quietly deleted, because a rationale that cannot be true is
144
+ * worse than a missing one and the shape of this mistake is easy to repeat. The
145
+ * argument ran: `expected` is a `bigint`; a `bigint` is not JSON-serialisable, and
146
+ * `effect@3.22`'s `Logger.json` throws outright on one nested in an annotation
147
+ * value and loses the whole line with it; therefore filling `message` would push
148
+ * that value class into the field every renderer reaches for first.
149
+ *
150
+ * The measurement is true and stays load-bearing. The inference from it is a
151
+ * category error. `message` is typed `string`, and TEMPLATE INTERPOLATION of a
152
+ * `bigint` yields its decimal digits, so nothing downstream of this getter can see
153
+ * a `bigint` through it. The hazard is about the values handed to a LOGGER as
154
+ * ANNOTATIONS — which is exactly where the projection supervisor applies it, in
155
+ * `shouldRestart`, whose `position` annotation is `String(stored)` — and it says
156
+ * nothing whatever about a string rendered from one. Nor was the substitute the
157
+ * old argument offered a substitute: the supervisor's `position` annotation is the
158
+ * progress signal it RE-READ after the failure, not this expectation. Meanwhile
159
+ * the cost of the omission was real and was being paid on every `Cause.pretty` of
160
+ * a superseded commit.
161
+ *
162
+ * What the correction does NOT touch is the other ground this class states, which
163
+ * stands unchanged: no STORED position is carried, because the recovery path
164
+ * re-reads and a carried one would already be stale. That decision is about a
165
+ * RECOVERY INPUT — what a caller might wrongly resume from. This getter is a
166
+ * human-readable label over a field the class already carries, and `expected` is
167
+ * exactly as stale in the label as it is in the payload: a reader is being told
168
+ * what the guard expected, never what to resume from.
169
+ *
170
+ * A GETTER rather than a `message` field, so `expected` stays the single
171
+ * declaration. Why a prototype accessor is safe here, and why a structured log of
172
+ * this fault still emits the same `key`/`expected`/`_tag` it always did, are set
173
+ * out once for all four of these getters on `ProjectionStoreError` above.
174
+ */
175
+ get message() {
176
+ return `the stored checkpoint no longer equals the expectation (${this.expected})`;
177
+ }
178
+ }
179
+ /**
180
+ * Bind a `ProjectionStore` to one `CheckpointKey`, producing the surface a read
181
+ * model is actually given.
182
+ *
183
+ * A FREE FUNCTION rather than a method on `ProjectionStore`, decided that way for
184
+ * two reasons beyond the obvious one (a single implementation, which every present
185
+ * and future backend then gets for nothing rather than re-deriving — and could
186
+ * re-derive WRONGLY, since a `forKey` that closed over the wrong key would be
187
+ * invisible until two read models started trading cursors).
188
+ *
189
+ * The sharper reason is DECORATORS. Wrapping a store is an established move here —
190
+ * a test's never-committing view store, a durability override — and every one of
191
+ * them is written `{ ...inner, commit: … }`. Were `forKey` a method, that spread
192
+ * would copy the INNER store's own `forKey`, whose closure still points at the
193
+ * inner store, so `forKey(decorated, key)` would silently route straight past the
194
+ * decoration. As a free function it reads `store.commit` off the value it is
195
+ * handed, so a decorated store decorates.
196
+ *
197
+ * The second reason is the port's implementation burden: `ProjectionStore` stays
198
+ * three methods, which is what a backend has to get right, and no implementation
199
+ * can drift on a member that is pure derivation.
200
+ *
201
+ * `Effect.suspend` on the two no-argument members preserves the port's per-call
202
+ * construction timing exactly: an implementation is entitled to build its effect
203
+ * when its method is called (the SQL store binds parameters there), so suspending
204
+ * keeps `yield* view.readCheckpoint` indistinguishable from
205
+ * `yield* store.readCheckpoint(key)` rather than freezing one construction for the
206
+ * store's lifetime.
207
+ */
208
+ export const forKey = (store, key) => ({
209
+ key,
210
+ durability: store.durability,
211
+ readCheckpoint: Effect.suspend(() => store.readCheckpoint(key)),
212
+ // Spelled out field by field rather than `{ key, ...args }`: the bound key is
213
+ // then the ONLY key in the call, which is the whole claim this façade makes, and
214
+ // no spread order or stray property can put a different one there.
215
+ commit: args => store.commit({
216
+ key,
217
+ expected: args.expected,
218
+ next: args.next,
219
+ viewWrites: args.viewWrites
220
+ }),
221
+ resetCheckpoint: Effect.suspend(() => store.resetCheckpoint(key))
222
+ });
223
+ //# sourceMappingURL=ProjectionStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectionStore.js","names":["Data","Effect","Schema","ProjectionId","String","pipe","nonEmptyString","brand","PartitionId","DEFAULT_PARTITION","make","checkpointKey","projection","partition","ProjectionStoreError","TaggedError","message","reason","CheckpointSuperseded","expected","forKey","store","key","durability","readCheckpoint","suspend","commit","args","next","viewWrites","resetCheckpoint"],"sources":["../../src/ProjectionStore.ts"],"sourcesContent":[null],"mappings":"AAsCA,SAASA,IAAI,EAAEC,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AAE7C;;;;;;;;;;AAUA,OAAO,MAAMC,YAAY,gBAAGD,MAAM,CAACE,MAAM,CAACC,IAAI,cAC5CH,MAAM,CAACI,cAAc,EAAE,eACvBJ,MAAM,CAACK,KAAK,CAAC,cAAc,CAAC,CAC7B;AAGD;;;;;;;;;;;;;;;AAeA,OAAO,MAAMC,WAAW,gBAAGN,MAAM,CAACE,MAAM,CAACC,IAAI,cAC3CH,MAAM,CAACI,cAAc,EAAE,eACvBJ,MAAM,CAACK,KAAK,CAAC,aAAa,CAAC,CAC5B;AAGD;AACA,OAAO,MAAME,iBAAiB,gBAAgBD,WAAW,CAACE,IAAI,CAAC,SAAS,CAAC;AA8BzE;;;;;AAKA,OAAO,MAAMC,aAAa,GAAGA,CAC3BC,UAAwB,EACxBC,SAAA,GAAyBJ,iBAAiB,MACvB;EAAEG,UAAU;EAAEC;AAAS,CAAE,CAAC;AAc/C;;;;;;;;;;;AAWA,OAAM,MAAOC,oBAAqB,sBAAQd,IAAI,CAACe,WAAW,CACxD,sBAAsB,CAItB;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgDA,IAAaC,OAAOA,CAAA;IAClB,OAAO,IAAI,CAACC,MAAM;EACpB;;AAGF;;;;;;;;;;;;;;;;;;;;;AAqBA,OAAM,MAAOC,oBAAqB,sBAAQlB,IAAI,CAACe,WAAW,CACxD,sBAAsB,CAItB;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDA,IAAaC,OAAOA,CAAA;IAClB,OAAO,2DAA2D,IAAI,CAACG,QAAQ,GAAG;EACpF;;AAsMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,OAAO,MAAMC,MAAM,GAAGA,CACpBC,KAAsB,EACtBC,GAAkB,MACQ;EAC1BA,GAAG;EACHC,UAAU,EAAEF,KAAK,CAACE,UAAU;EAC5BC,cAAc,EAAEvB,MAAM,CAACwB,OAAO,CAAC,MAAMJ,KAAK,CAACG,cAAc,CAACF,GAAG,CAAC,CAAC;EAC/D;EACA;EACA;EACAI,MAAM,EAASC,IAId,IACCN,KAAK,CAACK,MAAM,CAAC;IACXJ,GAAG;IACHH,QAAQ,EAAEQ,IAAI,CAACR,QAAQ;IACvBS,IAAI,EAAED,IAAI,CAACC,IAAI;IACfC,UAAU,EAAEF,IAAI,CAACE;GAClB,CAAC;EACJC,eAAe,EAAE7B,MAAM,CAACwB,OAAO,CAAC,MAAMJ,KAAK,CAACS,eAAe,CAACR,GAAG,CAAC;CACjE,CAAC","ignoreList":[]}
@@ -0,0 +1,29 @@
1
+ import { composeProjections } from '@kairos-es/core';
2
+ import { Ref } from 'effect';
3
+ /**
4
+ * Build the per-batch `apply` for a read model whose whole view is `view`.
5
+ *
6
+ * `slices` must be the SAME record the read model's subscription query is derived
7
+ * from, so the events the runner delivers are exactly the events these folds
8
+ * expect; `composeProjections` gates each slice on its own query before
9
+ * dispatching, so a slice never sees an event it did not ask for.
10
+ *
11
+ * The returned effect is infallible (`E = never`) and requirement-free
12
+ * (`R = never`), which is the whole point: handed to `ProjectionStore.commit` as
13
+ * `viewWrites`, it adds nothing to the commit's error or requirement channels, so
14
+ * the ONLY way that commit can fail is the checkpoint guard.
15
+ */
16
+ export const foldIntoRef = (slices, view) => {
17
+ // Composed ONCE, at wiring time, not per batch: the composition is pure and
18
+ // derives the merged query and the dispatch table, so rebuilding it on every
19
+ // batch would only burn allocations.
20
+ const composite = composeProjections(slices);
21
+ return batch =>
22
+ // One `Ref.update`, folding the whole batch inside the update function. Not a
23
+ // fold of `Ref.update`s: N updates are N observable states, so an interrupt or
24
+ // a concurrent reader could see the view half-way through a batch, and on a
25
+ // lost checkpoint guard a partly-applied batch would already be visible. One
26
+ // update makes the batch atomic in the same sense the checkpoint advance is.
27
+ Ref.update(view, state => batch.reduce((acc, decoded) => composite.evolve(acc, decoded), state));
28
+ };
29
+ //# sourceMappingURL=foldIntoRef.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foldIntoRef.js","names":["composeProjections","Ref","foldIntoRef","slices","view","composite","batch","update","state","reduce","acc","decoded","evolve"],"sources":["../../src/foldIntoRef.ts"],"sourcesContent":[null],"mappings":"AAqBA,SAEEA,kBAAkB,QAEb,iBAAiB;AAExB,SAAsBC,GAAG,QAAQ,QAAQ;AAEzC;;;;;;;;;;;;;AAaA,OAAO,MAAMC,WAAW,GAAGA,CACzBC,MAAS,EACTC,IAAgC,KAGN;EAC1B;EACA;EACA;EACA,MAAMC,SAAS,GAAGL,kBAAkB,CAACG,MAAM,CAAC;EAE5C,OAAQG,KAAK;EACX;EACA;EACA;EACA;EACA;EACAL,GAAG,CAACM,MAAM,CAACH,IAAI,EAAGI,KAAK,IACrBF,KAAK,CAACG,MAAM,CAAC,CAACC,GAAG,EAAEC,OAAO,KAAKN,SAAS,CAACO,MAAM,CAACF,GAAG,EAAEC,OAAO,CAAC,EAAEH,KAAK,CAAC,CACtE;AACL,CAAC","ignoreList":[]}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * The in-memory `ProjectionStore`: a `Ref` of checkpoints plus the exclusive
3
+ * critical section that makes the guarded advance atomic.
4
+ *
5
+ * This is NOT merely a test double. It is load-bearing for the in-process
6
+ * supervision restart, where the subscription dies (the event store's poll retry
7
+ * exhausted) but the process and its in-memory view survive: the restart must
8
+ * resume from the checkpoint rather than replay from `ORIGIN` into an
9
+ * already-populated view, and an ephemeral checkpoint stored beside an ephemeral
10
+ * view is exactly what makes that resume correct. It is also the whole
11
+ * all-in-memory permutation — tests, prototypes, client-side use — and the
12
+ * durable-log-with-in-memory-view mode's view store (dev, admin and in-flux
13
+ * slices, rebuilt from `ORIGIN` each boot).
14
+ *
15
+ * A factory `Effect`, deliberately not a `Layer`: the view store is selected PER
16
+ * MATERIALISATION — so a slice may start in memory and GRADUATE to Postgres, which
17
+ * `ReadModel.store` owns the claim about — and it is therefore a value handed to a
18
+ * read model, not a context tag that one store could claim process-wide.
19
+ *
20
+ * That selection is a FLOOR rather than a ceiling, and this store is on the common
21
+ * side of it: the two variants above are also available AT ONCE, an in-memory
22
+ * materialisation of a slice record beside a durable one from the SAME record —
23
+ * a Postgres table somebody queries with SQL, and an in-process view of the same
24
+ * slices holding one aggregate's hot figures with no round trip to reach them. That
25
+ * is `runProjections`, and every call of this factory is an independent store, so
26
+ * two materialisations wired that way share nothing but their slices. What they do
27
+ * not share is their FOLDS: each `apply` re-expresses one, so agreement between
28
+ * their figures is something a test demonstrates rather than something the wiring
29
+ * gives.
30
+ *
31
+ * The critical-section design mirrors `core`'s in-memory event store: one
32
+ * `Effect.makeSemaphore(1)` serialises the guarded advance, exactly as that store
33
+ * serialises check-then-append, and for the same reason — the check and the write
34
+ * must not be separable, and that atomicity IS the concurrency guarantee.
35
+ */
36
+ import { ORIGIN } from '@kairos-es/core';
37
+ import { Effect, Ref } from 'effect';
38
+ import { CheckpointSuperseded } from "./ProjectionStore.js";
39
+ /**
40
+ * Flatten a `CheckpointKey` into a map key.
41
+ *
42
+ * The separator is NUL. A `ProjectionId`/`PartitionId` is any non-empty string,
43
+ * so a printable separator (`:`, `/`, `|`) could be forged inside either half and
44
+ * two distinct keys would collide onto one checkpoint — one read model silently
45
+ * reading and advancing another's cursor. NUL cannot appear in a name any human
46
+ * or config file produces, so the flattening stays injective in practice without
47
+ * narrowing the id grammar.
48
+ *
49
+ * The other route to that same failure is a WIRING rather than a forged id: two
50
+ * materialisations handed this one store under equal keys, which would be two
51
+ * runners over one cursor. That one is caught before anything is forked, by the
52
+ * set-level collision rung of the construction gate in `projectionWiringFault.ts`,
53
+ * and its sentence names the two fixes. Nothing here can catch it — a store sees
54
+ * keys, never who is holding it.
55
+ */
56
+ const mapKey = key => `${key.projection}\u0000${key.partition}`;
57
+ /**
58
+ * Build an in-memory `ProjectionStore`.
59
+ *
60
+ * Every call is an independent store with its own checkpoints and its own
61
+ * semaphore, so two read models wired to two calls of this factory cannot
62
+ * interfere — and a test needing a fresh store just calls it again.
63
+ */
64
+ export const makeInMemoryProjectionStore = /*#__PURE__*/Effect.gen(function* () {
65
+ const checkpoints = yield* Ref.make(new Map());
66
+ const mutex = yield* Effect.makeSemaphore(1);
67
+ /** The stored position, or `ORIGIN` for a key never committed. */
68
+ const positionOf = (stored, key) => stored.get(mapKey(key)) ?? ORIGIN;
69
+ const readCheckpoint = key =>
70
+ // No lock needed: `Ref.get` is a single atomic read, and the guard that
71
+ // actually protects a commit is re-checked INSIDE the critical section
72
+ // below — a value read here is only ever an expectation to be guarded on,
73
+ // never a licence to write.
74
+ Effect.map(Ref.get(checkpoints), stored => positionOf(stored, key));
75
+ const commit = args =>
76
+ // The permit is acquired INTERRUPTIBLY (a fibre waiting its turn can still
77
+ // be torn down), and only the section itself is uninterruptible.
78
+ mutex.withPermits(1)(Effect.uninterruptible(Effect.gen(function* () {
79
+ const stored = positionOf(yield* Ref.get(checkpoints), args.key);
80
+ // The guard is checked FIRST, before the view writes run. There is no
81
+ // transaction here to roll anything back, so the ONLY way a lost
82
+ // guard can leave the view untouched is to lose before touching it.
83
+ // Check-then-act is safe despite `viewWrites` suspending in between,
84
+ // because the section is exclusive: no other fibre can observe or
85
+ // advance this checkpoint until the permit is released, so the value
86
+ // read here cannot go stale within the section.
87
+ //
88
+ // On a lost guard `viewWrites` was NEVER RUN, which is observably
89
+ // identical to the SQL store's rollback — the port's abort contract
90
+ // is met by omission rather than by undo. (The one case omission
91
+ // cannot cover is a `viewWrites` that fails part-way, which is why
92
+ // the port requires a non-transactional store's per-batch write to be
93
+ // a single atomic effect; see `ProjectionStore` and `foldIntoRef`.)
94
+ if (stored !== args.expected) {
95
+ return yield* new CheckpointSuperseded({
96
+ key: args.key,
97
+ expected: args.expected
98
+ });
99
+ }
100
+ yield* args.viewWrites;
101
+ // Uninterruptibility earns its keep here: an interrupt landing
102
+ // between the view writes and this advance would commit the batch's
103
+ // effects with the cursor left behind, and the next run would apply
104
+ // the same batch a second time. Copy-on-write so a concurrent reader
105
+ // holding the previous map sees a consistent snapshot.
106
+ yield* Ref.update(checkpoints, current => new Map(current).set(mapKey(args.key), args.next));
107
+ })));
108
+ const resetCheckpoint = key =>
109
+ // Under the same permit as `commit`, so a reset can never interleave with a
110
+ // guarded advance and leave the map half-updated relative to the view.
111
+ // Deleting rather than storing `ORIGIN` keeps "never committed" and "reset"
112
+ // one state, which is what the port promises the read-back is.
113
+ mutex.withPermits(1)(Ref.update(checkpoints, current => {
114
+ const next = new Map(current);
115
+ next.delete(mapKey(key));
116
+ return next;
117
+ }));
118
+ // Annotated at the definition site rather than only through the exported
119
+ // signature, per the rule on `ProjectionStore` itself.
120
+ const store = {
121
+ // Dies with the process, which is the R2 input: an ephemeral view is legal
122
+ // over either an ephemeral or a durable log (it just re-catches-up), so
123
+ // this value never trips the durability-ordering check.
124
+ durability: 'ephemeral',
125
+ readCheckpoint,
126
+ commit,
127
+ resetCheckpoint
128
+ };
129
+ return store;
130
+ });
131
+ //# sourceMappingURL=inMemoryProjectionStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inMemoryProjectionStore.js","names":["ORIGIN","Effect","Ref","CheckpointSuperseded","mapKey","key","projection","partition","makeInMemoryProjectionStore","gen","checkpoints","make","Map","mutex","makeSemaphore","positionOf","stored","get","readCheckpoint","map","commit","args","withPermits","uninterruptible","expected","viewWrites","update","current","set","next","resetCheckpoint","delete","store","durability"],"sources":["../../src/inMemoryProjectionStore.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAASA,MAAM,QAAuB,iBAAiB;AACvD,SAASC,MAAM,EAAEC,GAAG,QAAQ,QAAQ;AAEpC,SAASC,oBAAoB,QAAQ,sBAAmB;AAExD;;;;;;;;;;;;;;;;;AAiBA,MAAMC,MAAM,GAAIC,GAAkB,IAChC,GAAGA,GAAG,CAACC,UAAU,SAASD,GAAG,CAACE,SAAS,EAAE;AAE3C;;;;;;;AAOA,OAAO,MAAMC,2BAA2B,gBACtCP,MAAM,CAACQ,GAAG,CAAC,aAAS;EAClB,MAAMC,WAAW,GAAG,OAAOR,GAAG,CAACS,IAAI,CACjC,IAAIC,GAAG,EAAE,CACV;EACD,MAAMC,KAAK,GAAG,OAAOZ,MAAM,CAACa,aAAa,CAAC,CAAC,CAAC;EAE5C;EACA,MAAMC,UAAU,GAAGA,CACjBC,MAAqC,EACrCX,GAAkB,KACLW,MAAM,CAACC,GAAG,CAACb,MAAM,CAACC,GAAG,CAAC,CAAC,IAAIL,MAAM;EAEhD,MAAMkB,cAAc,GAClBb,GAAkB;EAElB;EACA;EACA;EACA;EACAJ,MAAM,CAACkB,GAAG,CAACjB,GAAG,CAACe,GAAG,CAACP,WAAW,CAAC,EAAGM,MAAM,IAAKD,UAAU,CAACC,MAAM,EAAEX,GAAG,CAAC,CAAC;EAEvE,MAAMe,MAAM,GAAUC,IAKrB;EACC;EACA;EACAR,KAAK,CAACS,WAAW,CAAC,CAAC,CAAC,CAClBrB,MAAM,CAACsB,eAAe,CACpBtB,MAAM,CAACQ,GAAG,CAAC,aAAS;IAClB,MAAMO,MAAM,GAAGD,UAAU,CAAC,OAAOb,GAAG,CAACe,GAAG,CAACP,WAAW,CAAC,EAAEW,IAAI,CAAChB,GAAG,CAAC;IAEhE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIW,MAAM,KAAKK,IAAI,CAACG,QAAQ,EAAE;MAC5B,OAAO,OAAO,IAAIrB,oBAAoB,CAAC;QACrCE,GAAG,EAAEgB,IAAI,CAAChB,GAAG;QACbmB,QAAQ,EAAEH,IAAI,CAACG;OAChB,CAAC;IACJ;IAEA,OAAOH,IAAI,CAACI,UAAU;IAEtB;IACA;IACA;IACA;IACA;IACA,OAAOvB,GAAG,CAACwB,MAAM,CAAChB,WAAW,EAAGiB,OAAO,IACrC,IAAIf,GAAG,CAACe,OAAO,CAAC,CAACC,GAAG,CAACxB,MAAM,CAACiB,IAAI,CAAChB,GAAG,CAAC,EAAEgB,IAAI,CAACQ,IAAI,CAAC,CAClD;EACH,CAAC,CAAC,CACH,CACF;EAEH,MAAMC,eAAe,GAAIzB,GAAkB;EACzC;EACA;EACA;EACA;EACAQ,KAAK,CAACS,WAAW,CAAC,CAAC,CAAC,CAClBpB,GAAG,CAACwB,MAAM,CAAChB,WAAW,EAAGiB,OAAO,IAAI;IAClC,MAAME,IAAI,GAAG,IAAIjB,GAAG,CAACe,OAAO,CAAC;IAC7BE,IAAI,CAACE,MAAM,CAAC3B,MAAM,CAACC,GAAG,CAAC,CAAC;IACxB,OAAOwB,IAAI;EACb,CAAC,CAAC,CACH;EAEH;EACA;EACA,MAAMG,KAAK,GAAoB;IAC7B;IACA;IACA;IACAC,UAAU,EAAE,WAAW;IACvBf,cAAc;IACdE,MAAM;IACNU;GACD;EACD,OAAOE,KAAK;AACd,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @kairos-es/read — the read side: a query-driven projection runner over the
3
+ * store's `subscribe` contract, and a scalar checkpoint committed atomically with
4
+ * the read model's own writes (ADR-0007).
5
+ *
6
+ * Platform-neutral by design: peers are `effect` plus `@kairos-es/core` and
7
+ * `@kairos-es/codec` only — the same peer shape as the codec — so the
8
+ * all-in-memory permutation, a property of one MATERIALISATION's (log, view)
9
+ * pairing and including client-side use, needs no database
10
+ * dependency. Every dependency-bearing backend is its own package, which is why
11
+ * the SQL `ProjectionStore` lives in `@kairos-es/read-postgres` rather than behind
12
+ * a sub-entry here: a sub-entry shares one dependency surface, whereas a separate
13
+ * package isolates a new one (`@effect/sql`).
14
+ *
15
+ * ## Where each rationale lives
16
+ *
17
+ * This module re-exports; it does not argue. Every argument the read side rests
18
+ * on is written ONCE, at the definition site a reader would reach for anyway,
19
+ * because a rationale kept in N places is a rationale a correction has to find N
20
+ * times. What follows is the index, R1 and R2 included: each of those two has a
21
+ * module that owns it, so each gets a pointer here like everything else. R3 is the
22
+ * one exception, and the last section says why.
23
+ *
24
+ * - **The log seam is `DcbEventStore.subscribe`, and it belongs entirely to the
25
+ * event store.** `store-postgres` owns LISTEN/NOTIFY as a wake source over
26
+ * `core`'s shared poll machine, `store-sqlite` owns no wake source and rides
27
+ * that poll alone, and `core`'s in-memory layer owns its append `PubSub` as a
28
+ * wake source over that same machine; all three sit behind the
29
+ * identical `subscribe(query, after)` contract, so a projection never learns
30
+ * which it is running against. Nothing in this package adds to that contract —
31
+ * the one claim below that no other module is placed to make.
32
+ * - **The view seam is `ProjectionStore`** — one port rather than two, `commit`
33
+ * taking the view writes as an ARGUMENT, `CheckpointSuperseded` as an error
34
+ * rather than a returned outcome, and `ProjectionStoreError` inverting the store
35
+ * contract's defect rule. `ProjectionStore.ts`'s module doc. The multi-key port
36
+ * against the keyed façade a read model is handed is on `KeyedProjectionStore`
37
+ * and on `forKey` in the same module; the single-atomic-effect requirement a
38
+ * NON-TRANSACTIONAL store's caller must honour is on the `ProjectionStore`
39
+ * interface itself, and `foldIntoRef.ts` is that shape.
40
+ * - **R1, atomicity** — a read model's checkpoint lives WITH its view, in a store
41
+ * where the two can be committed atomically, which is what `commit` is. The
42
+ * argument is on the `ProjectionStore` interface in `ProjectionStore.ts` (R1 is
43
+ * what that interface IS), together with what a view offering no transaction at
44
+ * all falls back to: at-least-once, with POSITION-KEYED idempotent writes, a
45
+ * recommendation because the port cannot check it.
46
+ * - **R2, durability ordering** — checkpoint durability must not EXCEED event-log
47
+ * durability, so a DURABLE view over an EPHEMERAL log is a defect rejected at
48
+ * construction rather than a view that silently freezes. The view store declares
49
+ * its half on the `ProjectionStore` it implements; the log's half, the silent
50
+ * failure the rule prevents, and why the log's class is ONE context tag beside
51
+ * the store layer rather than a field per read model are all in
52
+ * `EventLogDurability.ts`.
53
+ * - **How these errors RENDER themselves** — why `ProjectionStoreError` derives its
54
+ * `message` from `reason` and `CheckpointSuperseded` derives one from `expected`
55
+ * (including the `bigint` argument that once kept the field empty and why it was a
56
+ * category error), how far filling that field reaches (every reader of the error,
57
+ * not one log line) and what it leaves untouched: the two classes in
58
+ * `ProjectionStore.ts`. `PipelineDied` in `ProjectionRunner.ts` and
59
+ * `ProjectionStalled` in `superviseOnProgress.ts` are the other two and point
60
+ * there.
61
+ * - **Why the view store is a VALUE and not a `Context.Tag`**, and what a slice's
62
+ * graduation from memory to Postgres does and does not change: `ReadModel.store`
63
+ * in `runProjection.ts`. It is demonstrated rather than asserted, by
64
+ * `examples/course-subscriptions`'s `src/courseRosterSql.ts` and by
65
+ * `@kairos-es/read-postgres`'s `test/courseRosterGraduation.test.ts`.
66
+ * - **The in-memory implementation, and why it is not merely a test double:**
67
+ * `inMemoryProjectionStore.ts`.
68
+ * - **The runner** — the pipeline stage by stage, and why supervision lives next
69
+ * door: `ProjectionRunner.ts`, which holds the SUBSTRATE both entry points sit on
70
+ * and no entry point of its own. Its two option sets are documented on
71
+ * `ProjectionPipelineOptions` there and on `ProjectionSupervisionOptions` in
72
+ * `superviseOnProgress.ts`; `ProjectionRunnerOptions` says why their union is
73
+ * FLAT and why every figure is bounded rather than merely documented.
74
+ * - **ONE materialisation of one read model** — `runProjection` and the
75
+ * `projectionLayer` over it: `runProjection.ts`, which also says why the singular
76
+ * and the plural entry points are two modules over one substrate and why neither
77
+ * calls the other.
78
+ * - **N MATERIALISATIONS of one read model** — one slice record and one
79
+ * `ProjectionId` for the call, one view store and one runner each, which is what
80
+ * `runProjections` is: `runProjections.ts`. It carries the guarantee at exactly
81
+ * its strength (one query, one decode, N applies — never "they cannot drift",
82
+ * since a SQL `apply` re-implements the fold and agreement between materialised
83
+ * figures is empirical), why the guarantee is construction-scoped, why each entry
84
+ * hands over an UNBOUND `ProjectionStore` while the identity is named once for the
85
+ * call, why the tuning and the two supervision hooks are PER ENTRY, and why there
86
+ * is no `projectionsLayer`. The wiring that is its OWN — two materialisations
87
+ * sharing one store under one resolved `CheckpointKey` — is the set-level rung of
88
+ * the construction gate below, with the two bounds it deliberately does not reach;
89
+ * the gate's other four run in the same pass — two per entry, two once over the
90
+ * shared query and slice record — so no refused call forks anything, whichever
91
+ * rung and whichever entry it was.
92
+ * - **The supervisor** — retry while an externally observed signal moves, give up
93
+ * when it stops; why a `CheckpointSuperseded` loser resynchronises instead of
94
+ * tripping the breaker; why the two hooks are the primary observability channel
95
+ * and the log lines a thin lossy default; why it holds no rendering of a
96
+ * caller's `E` at all, the label being the fault's own `message`; and the
97
+ * wall-clock arithmetic behind the no-progress budget: `superviseOnProgress.ts`.
98
+ * `superviseOnProgress` ITSELF is package-internal and deliberately not exported
99
+ * below — it is the projection runner's supervisor, not a general combinator, and
100
+ * that module records both why and what generalising it would cost. Three
101
+ * symbols from it are exported, each because the PUBLIC surface names it:
102
+ * `ProjectionStalled` is `ProjectionRunner.fibre`'s error channel,
103
+ * `ProjectionRunnerOptions` extends `ProjectionSupervisionOptions`, and tuning
104
+ * the no-progress budget means calling `MaxNoProgressRestarts.make(n)`.
105
+ * - **One writer per MATERIALISATION, and why one read model's stream is never
106
+ * sharded:** ADR-0007, with the partition dimension's own half on `PartitionId`.
107
+ * Per materialisation rather than per read model, because a read model may have
108
+ * several — N of them are N single writers, never shards of one stream.
109
+ * Where apply THROUGHPUT rather than isolation is the constraint, ADR-0007 also
110
+ * carries the escape hatch — coalesce a batch per target row inside the one
111
+ * commit — and there is no library symbol for it, because it is a fold the read
112
+ * model performs over its own batch.
113
+ * - **The tagless-slice rule** — every read-model slice carries at least one tag
114
+ * (a `system:…` tag for a genuinely global slice), enforced at construction by
115
+ * two checks that cannot stand in for each other, since a lone tagless slice
116
+ * composes to a query the store WOULD serve: both are rungs of the wiring gate
117
+ * `projectionWiringFault.ts`, which argues the rules and the order they are judged
118
+ * in, and which is consulted from the one `prepareProjection` phase both runner
119
+ * entry points run before either forks anything.
120
+ * - **Running a read model in a test** — the `@kairos-es/read/testing` subpath
121
+ * (`testing.ts`), which also states its boundary against
122
+ * `@kairos-es/projection-store-contract-tests`.
123
+ * - **The narrative version of all of it** is this package's `README.md`; the
124
+ * decision it implements is ADR-0007.
125
+ *
126
+ * ## R3, the legal permutations, and several materialisations of one read model
127
+ *
128
+ * The one thing this file ARGUES rather than indexes. R1 and R2 each have a module
129
+ * that owns them, pointed at above; R3 has none, because it is a property of how
130
+ * read models are composed into a SUBSCRIPTION and no single module holds that.
131
+ *
132
+ * **R3, composition bound.** Read models sharing one subscription share one cursor
133
+ * and one checkpoint, hence by R1 one view store and one durability class. So
134
+ * per-read-model view selection means one runner per view-store GROUP, not one per
135
+ * read model — and rebuilding one member of a composed bundle means rebuilding the
136
+ * bundle (or running a transient dedicated runner from `ORIGIN`). Composing is
137
+ * still the default, because the NOTIFY channel is per store, not per query.
138
+ *
139
+ * **What R3 does NOT say.** It does not bound how many times ONE read model may be
140
+ * materialised. R3 is about sharing a SUBSCRIPTION, and several materialisations of
141
+ * one read model deliberately do not share one: they pay N subscriptions, N cursors
142
+ * and N checkpoints precisely BECAUSE R1 and R2 forbid the sharing — one atomic
143
+ * `commit` cannot span two stores, and one checkpoint cannot carry two durability
144
+ * classes. So per-read-model view-store selection is a FLOOR rather than a ceiling.
145
+ * One slice record may feed a durable Postgres materialisation and an ephemeral
146
+ * in-memory one AT ONCE, wired by `runProjections`, which holds one `slices` value
147
+ * and one `ProjectionId` across them so their query and their decode cannot diverge.
148
+ * What it does not hold is the FOLDS — each `apply` re-expresses the fold against
149
+ * its own store, and a SQL one writing rows is not `foldIntoRef` — so agreement
150
+ * between two materialised figures is EMPIRICAL, something a test demonstrates, and
151
+ * never something the wiring gives you. The runners do not contend either: each
152
+ * owns its own checkpoint in its own store, so the guarded compare-and-set
153
+ * arbitrates with no lease, no lock and no coordinator.
154
+ *
155
+ * That leaves exactly THREE legal (log, view) permutations: all-in-memory (tests,
156
+ * prototypes, client-side); a durable log with an in-memory view (dev, low-traffic
157
+ * and admin views, in-flux slices — rebuilt from `ORIGIN` each boot, which is
158
+ * affordable at the event counts that are its remit and a reason to graduate a
159
+ * slice once it bakes); and a durable log with a durable view, the production
160
+ * default. Each is a property of ONE MATERIALISATION's pairing. Mixing is a
161
+ * DEPLOYMENT property rather than a fourth permutation: one deployment can run the
162
+ * second and third side by side against the same log — for two different read
163
+ * models, which is the case R3 bounds when they try to share a subscription, or for
164
+ * two materialisations of ONE read model, which R3 never reaches because they never
165
+ * share one.
166
+ *
167
+ * **A third KIND of materialisation sits outside all of this.** A read-time
168
+ * materialisation — `modelAtHead`, in `@kairos-es/codec` — folds the log per query
169
+ * and has no table, no checkpoint, no runner and no view store, so no permutation,
170
+ * no rung of the construction gate and no durability rule applies to it, R2 being
171
+ * VACUOUS where there is no checkpoint whose durability could outlive a log. It is
172
+ * consistent as of the `head` its read returned, which trails nothing, so it is the
173
+ * MOST current view of a slice record and the maintained table beside it is the
174
+ * stale one. Where a read model is small enough to fold at query time, that is a
175
+ * reason not to maintain it at all.
176
+ */
177
+ export { DurableEventLog, EphemeralEventLog, EventLogDurability } from "./EventLogDurability.js";
178
+ export { foldIntoRef } from "./foldIntoRef.js";
179
+ export { makeInMemoryProjectionStore } from "./inMemoryProjectionStore.js";
180
+ export { BatchSize, PipelineDied } from "./ProjectionRunner.js";
181
+ export { CheckpointSuperseded, checkpointKey, DEFAULT_PARTITION, forKey, PartitionId, ProjectionId, ProjectionStoreError } from "./ProjectionStore.js";
182
+ export { projectionLayer, runProjection } from "./runProjection.js";
183
+ export { runProjections } from "./runProjections.js";
184
+ export { MaxNoProgressRestarts, ProjectionStalled } from "./superviseOnProgress.js";
185
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["DurableEventLog","EphemeralEventLog","EventLogDurability","foldIntoRef","makeInMemoryProjectionStore","BatchSize","PipelineDied","CheckpointSuperseded","checkpointKey","DEFAULT_PARTITION","forKey","PartitionId","ProjectionId","ProjectionStoreError","projectionLayer","runProjection","runProjections","MaxNoProgressRestarts","ProjectionStalled"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgLA,SACEA,eAAe,EACfC,iBAAiB,EACjBC,kBAAkB,QACb,yBAAsB;AAC7B,SAASC,WAAW,QAAQ,kBAAe;AAC3C,SAASC,2BAA2B,QAAQ,8BAA2B;AACvE,SACEC,SAAS,EACTC,YAAY,QAIP,uBAAoB;AAC3B,SAEEC,oBAAoB,EACpBC,aAAa,EACbC,iBAAiB,EAEjBC,MAAM,EAENC,WAAW,EACXC,YAAY,EAEZC,oBAAoB,QACf,sBAAmB;AAC1B,SACEC,eAAe,EAEfC,aAAa,QACR,oBAAiB;AACxB,SAGEC,cAAc,QACT,qBAAkB;AACzB,SACEC,qBAAqB,EACrBC,iBAAiB,QAEZ,0BAAuB","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "module",
3
+ "sideEffects": []
4
+ }