@kairos-es/codec 1.0.0-alpha.0 → 1.0.0-alpha.2

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 (44) hide show
  1. package/README.md +75 -3
  2. package/dist/cjs/decider.js +44 -69
  3. package/dist/cjs/decider.js.map +1 -1
  4. package/dist/cjs/decision.js +45 -0
  5. package/dist/cjs/decision.js.map +1 -0
  6. package/dist/cjs/decisionModel.js +76 -20
  7. package/dist/cjs/decisionModel.js.map +1 -1
  8. package/dist/cjs/index.js +63 -0
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/precondition.js +191 -0
  11. package/dist/cjs/precondition.js.map +1 -0
  12. package/dist/cjs/readLedger.js +224 -0
  13. package/dist/cjs/readLedger.js.map +1 -0
  14. package/dist/dts/decider.d.ts +57 -8
  15. package/dist/dts/decider.d.ts.map +1 -1
  16. package/dist/dts/decision.d.ts +161 -0
  17. package/dist/dts/decision.d.ts.map +1 -0
  18. package/dist/dts/decisionModel.d.ts +33 -13
  19. package/dist/dts/decisionModel.d.ts.map +1 -1
  20. package/dist/dts/index.d.ts +69 -0
  21. package/dist/dts/index.d.ts.map +1 -1
  22. package/dist/dts/precondition.d.ts +244 -0
  23. package/dist/dts/precondition.d.ts.map +1 -0
  24. package/dist/dts/readLedger.d.ts +263 -0
  25. package/dist/dts/readLedger.d.ts.map +1 -0
  26. package/dist/esm/decider.js +42 -67
  27. package/dist/esm/decider.js.map +1 -1
  28. package/dist/esm/decision.js +37 -0
  29. package/dist/esm/decision.js.map +1 -0
  30. package/dist/esm/decisionModel.js +77 -21
  31. package/dist/esm/decisionModel.js.map +1 -1
  32. package/dist/esm/index.js +69 -0
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/precondition.js +182 -0
  35. package/dist/esm/precondition.js.map +1 -0
  36. package/dist/esm/readLedger.js +212 -0
  37. package/dist/esm/readLedger.js.map +1 -0
  38. package/package.json +2 -2
  39. package/src/decider.ts +98 -19
  40. package/src/decision.ts +174 -0
  41. package/src/decisionModel.ts +87 -23
  42. package/src/index.ts +87 -0
  43. package/src/precondition.ts +371 -0
  44. package/src/readLedger.ts +394 -0
@@ -0,0 +1,263 @@
1
+ /**
2
+ * The ambient read ledger: the hops a bracket walked, and the single derivation
3
+ * that turns them into one `AppendCondition`.
4
+ *
5
+ * ## The gap this closes, and why the fix has to be ambient
6
+ *
7
+ * A decision model whose second query cannot be written until an earlier read has
8
+ * been folded — the identifier it needs is data that fold produced — guards its
9
+ * append on only the LAST read if it reaches for `buildDecisionModel` there. The
10
+ * append then succeeds on a premise that is no longer true, and nothing warns: it
11
+ * compiles, it runs, and it is silent. Two things must both change for the
12
+ * condition to be sound, and fixing either alone still lets the stale write
13
+ * through. Keeping the union of queries but taking the last head misses, because
14
+ * the invalidating event lands in the window between the two heads. Keeping the
15
+ * earliest head but taking the last query misses, because the invalidating event
16
+ * fails that query's TYPE arm before its tag arm is reached — which is why adding
17
+ * a tag to the last query cannot recover it.
18
+ *
19
+ * The ledger is an OPTIONAL AMBIENT SERVICE rather than a scope object a caller
20
+ * threads, and that is the whole design. A scope object's guarantee is "every read
21
+ * through this object", which is not "every read": `modelAtHead` stays exported
22
+ * and stays in scope inside the bracket, so a read that reached for the familiar
23
+ * name — or that happened inside a helper the body merely called — drops a hop
24
+ * silently. Here `modelAtHead` ITSELF records, so a read is recorded whatever
25
+ * symbol performed it and at whatever call depth. `Effect.serviceOption` is what
26
+ * makes that additive: it reads the service if present and adds NOTHING to the
27
+ * requirement channel, so `modelAtHead`'s published signature is untouched and no
28
+ * existing caller provides anything.
29
+ *
30
+ * ## Why the query is recorded eagerly and the head only on completion
31
+ *
32
+ * A `Hop` is `{ query, head? }` and the two halves are recorded at different
33
+ * moments: the query before a single event is streamed, the head once the fold has
34
+ * run to completion. So a read that opened and never finished widens the guard
35
+ * without contributing a floor, and both halves fail safe.
36
+ *
37
+ * The laziness is deliberate protection rather than an accident of where the line
38
+ * sits. Recording the head at read-return is available and MUST NOT be done: a
39
+ * read that failed part-way folded only a PREFIX, and a head recorded for it is a
40
+ * claim the hop did not earn — the floor would sit above events that read never
41
+ * folded, and nothing would refuse an append those events invalidate.
42
+ *
43
+ * ## An invalidated ledger must REFUSE to derive, never derive narrowly
44
+ *
45
+ * Nothing here evicts a hop, so there is no eviction policy to specify — but the
46
+ * rule is stated now because the moment a bound is added, and an unbounded walk on
47
+ * a large log will want one, the tempting implementation is to drop the oldest hop
48
+ * and carry on. A ledger that has lost hops must become a poison value and refuse
49
+ * to produce a condition at all. Deriving from a PARTIAL set is worse than
50
+ * refusing, because it looks like it worked: an emptied ledger produces a NARROWER
51
+ * condition and a silent success, which is the original hazard wearing a fix's
52
+ * name. The valve is coarsen, then poison. Never forget.
53
+ *
54
+ * ## What is deliberately NOT exported from the package
55
+ *
56
+ * `deriveLayeredCondition` is PACKAGE-internal and reaches no entry point. Two
57
+ * traps stay reachable for a caller who assembles a condition by hand, both
58
+ * demonstrated: concatenating query items drops a wildcard hop, an empty item list
59
+ * being absorbing under OR while an empty concatenation contributes nothing; and a
60
+ * MAXIMUM over heads reads as naturally as a minimum, two reducers of identical
61
+ * signature where "the position I read at" argues just as fluently for the later
62
+ * one. Offering the derivation as a public step would put both back within reach
63
+ * of the callers the bracket exists to protect.
64
+ */
65
+ import type { AnyProjection, AppendCondition, Position, Query } from '@kairos-es/core';
66
+ import { Context, Effect, Layer, Option } from 'effect';
67
+ /**
68
+ * One recorded read: the query it rode, and the head it observed.
69
+ *
70
+ * `head` is a genuinely OPTIONAL key and not `Position | undefined`, because
71
+ * `exactOptionalPropertyTypes` is on and the absent case must be an absent key.
72
+ * Absent means the read did not complete, which the derivation below treats as
73
+ * the widest possible floor.
74
+ */
75
+ export interface Hop {
76
+ readonly query: Query;
77
+ readonly head?: Position;
78
+ }
79
+ declare const ReadLedger_base: Context.TagClass<ReadLedger, "@kairos-es/codec/ReadLedger", {
80
+ /**
81
+ * File a query, and hand back the effect that will fill in its head.
82
+ *
83
+ * Two phases and not one `record(hop)`, which is what the eager/lazy split
84
+ * above needs: a one-shot recorder cannot express a hop whose head never
85
+ * arrives, and that is the case the whole fail-safe direction turns on.
86
+ *
87
+ * The second phase comes back as a CLOSURE rather than as a slot number the
88
+ * caller carries to a second method. The closure holds its own slot, so
89
+ * nothing threads an index through the reader, nothing looks the service up a
90
+ * second time to spend it, and there is no slot value for a caller to hold
91
+ * wrongly.
92
+ */
93
+ readonly record: (query: Query) => Effect.Effect<CloseHop>;
94
+ readonly hops: Effect.Effect<ReadonlyArray<Hop>>;
95
+ }>;
96
+ /**
97
+ * The ambient ledger, present only inside a bracket.
98
+ *
99
+ * ⚠ A RECORDING path reads it with `Effect.serviceOption` and never
100
+ * `yield* ReadLedger`: its absence is the normal case there, and costing the
101
+ * requirement channel nothing is exactly what makes this change additive.
102
+ * `modelAtHead` is that path and is why the rule exists.
103
+ *
104
+ * A BRACKET-ONLY function is the opposite case and yields the tag deliberately.
105
+ * `decideOnly` does, and naming the requirement is what turns *"call me inside a
106
+ * bracket"* from a runtime defect into a compile error — presence being the one
107
+ * direction the type system can decide here. So the test is what the function
108
+ * means outside a bracket: nothing (record ambiently), or nothing sensible
109
+ * (require it).
110
+ *
111
+ * Recording is two-phase, and `record`'s own doc below owns why and what shape the
112
+ * second phase takes.
113
+ */
114
+ export declare class ReadLedger extends ReadLedger_base {
115
+ }
116
+ /**
117
+ * Fill in a recorded hop's head. Never called if the read does not complete.
118
+ *
119
+ * On the package's public surface because it is already ON it: `record` returns
120
+ * one, `ReadLedger` is exported, so a consumer holding the tag holds this type and
121
+ * could not name it. `Hop` was exported and this was not — the same interface, two
122
+ * treatments.
123
+ */
124
+ export type CloseHop = (head: Position) => Effect.Effect<void>;
125
+ declare const NoReadsRecorded_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 & {
126
+ readonly _tag: "NoReadsRecorded";
127
+ } & Readonly<A>;
128
+ /**
129
+ * Raised when a bracket closes having recorded no read at all.
130
+ *
131
+ * On the ERROR channel and deliberately split from the nested-append DEFECT,
132
+ * because the two are different mistakes. An empty bracket can arise from a
133
+ * data-dependent branch that produces events without reading — a genuinely
134
+ * unconditional creation, written inside a bracket by mistake — where a nested
135
+ * append is always a wiring fault. An error-channel value lets a caller decide
136
+ * which it is.
137
+ *
138
+ * Its job is narrower than it looks, and the narrowing is worth stating because
139
+ * the wider reading is the tempting one. Once a bracket's guard is itself a fold,
140
+ * a body that reads nothing still has a hop, so this cannot fire — it is the guard
141
+ * against an unguarded UNCONDITIONAL bracket, not against an unguarded bracket. A
142
+ * bracket that looks guarded and is not is NOT what this catches and never was,
143
+ * and nothing else catches it either — `precondition.ts` owns that gap and the
144
+ * rule a reader has to keep in its place. A refusal taken BEFORE reading is not
145
+ * this case either: it short-circuits and surfaces as the refusal.
146
+ */
147
+ export declare class NoReadsRecorded extends NoReadsRecorded_base<Record<never, never>> {
148
+ /**
149
+ * The sentence, as the `Error` field that says why — so the fault renders
150
+ * ITSELF, exactly as this package's other two errors do (`errors.ts` owns the
151
+ * argument for the mechanism and it is not restated here).
152
+ *
153
+ * A getter over a constant rather than a constructor field, because there is no
154
+ * datum to vary: every raise of this error means one thing.
155
+ */
156
+ get message(): string;
157
+ }
158
+ declare const TaglessSliceInLedger_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 & {
159
+ readonly _tag: "TaglessSliceInLedger";
160
+ } & Readonly<A>;
161
+ /**
162
+ * Raised when recording a hop would leave the bracket's accumulated union
163
+ * unservable.
164
+ *
165
+ * The store refuses a multi-item query in which any item is tagless, and it
166
+ * refuses it by THROWING inside the append decorator, which becomes a defect:
167
+ * uncatchable, one call and one layer away from the `build()` that chose no tags.
168
+ * A slice built without tags defaults to none, so it is perfectly servable alone
169
+ * and kills the append the moment it is unioned. This moves that failure onto the
170
+ * error channel, to the read that caused it, naming the record entry to fix.
171
+ *
172
+ * It refuses an unservable UNION and not every tagless hop, which is the easy
173
+ * mistake to make in the other direction: one tagless slice read on its own is
174
+ * servable and must keep working. Two cases reach it — a tagless hop unioned with
175
+ * another, and a single hop whose own RECORD holds two tagless slices, which
176
+ * composes to a multi-item query with no second hop needed.
177
+ */
178
+ export declare class TaglessSliceInLedger extends TaglessSliceInLedger_base<{
179
+ readonly sliceKey: string;
180
+ }> {
181
+ /** The offending slice and the remedy, in one sentence. */
182
+ get message(): string;
183
+ }
184
+ /**
185
+ * The ONE derivation: the union of every query walked, at the earliest head
186
+ * observed. Package-internal, for the reason the module doc gives.
187
+ *
188
+ * The union goes through core's own `unionQueries` — the merge
189
+ * `composeProjections` folds a record through, reached without one — and never a
190
+ * spread of `items`, because a wildcard hop (`items: []`) is ABSORBING under OR
191
+ * while a spread of an empty list contributes nothing, so hand-rolling the union
192
+ * narrows a match-everything hop to its sibling, silently and in the unsafe
193
+ * direction. A
194
+ * repeated or overlapping hop is harmless because this derives ONCE from a merged
195
+ * set and matching is idempotent over an OR; that property belongs to deriving
196
+ * once and would be lost by a refactor to per-hop evaluation.
197
+ *
198
+ * ## When any hop is headless the floor is `ORIGIN`, and the alternative is unsound
199
+ *
200
+ * The earliest head that actually ARRIVED is refused, and is named here as refused
201
+ * rather than merely not chosen, because it is what a reader writes by accident
202
+ * when told to take the earliest head. With one headless hop among completed hops
203
+ * the union looks right and the floor betrays it, sitting already above the
204
+ * invalidating event.
205
+ *
206
+ * `ORIGIN`'s cost is measured rather than described: it refuses every append to
207
+ * any identity that already has history, so it is not a conservative default but a
208
+ * hard stop. It is affordable only because conflict retry wraps the bracket from
209
+ * OUTSIDE and re-reads, which is the rule the whole shape depends on.
210
+ */
211
+ export declare const deriveLayeredCondition: (hops: ReadonlyArray<Hop>) => AppendCondition | undefined;
212
+ /**
213
+ * A fresh ledger and the `Layer` providing it: a `Ref` of hops behind the two-phase
214
+ * `record` the service declares.
215
+ *
216
+ * The hops effect is handed back beside the layer because the bracket reads them
217
+ * AFTER the body has run and the layer is out of scope, which is the same moment it
218
+ * stops being able to reach the service.
219
+ */
220
+ export declare const makeReadLedger: Effect.Effect<{
221
+ readonly layer: Layer.Layer<ReadLedger>;
222
+ readonly hops: Effect.Effect<ReadonlyArray<Hop>>;
223
+ }>;
224
+ /**
225
+ * File a query into the ambient ledger if there is one, and hand back the effect
226
+ * that completes it. Outside a bracket the whole thing is a no-op, and the caller
227
+ * still writes one line rather than a conditional.
228
+ */
229
+ export declare const recordQuery: (query: Query) => Effect.Effect<CloseHop>;
230
+ /**
231
+ * Refuse a read that would leave the bracket's accumulated union unservable,
232
+ * BEFORE the read is paid for.
233
+ *
234
+ * Servability is answerable from the queries alone, so the check runs when a hop is
235
+ * recorded rather than at append time — the eager half of the eager/lazy split doing
236
+ * one more job. Outside a bracket nothing is unioned and nothing is unservable, so
237
+ * the ordinary single-hop caller is untaxed.
238
+ *
239
+ * It judges the PROSPECTIVE UNION and asks core for the verdict, which is what makes
240
+ * both failing shapes one case rather than two. An earlier version tested each
241
+ * recorded query separately and treated a FIRST hop as unconditionally fine — but a
242
+ * hop is a RECORD, and a record of two tagless slices composes to a multi-item query
243
+ * that is unservable on its own. That read died at the store, uncatchably, on the
244
+ * exact path this error exists to keep off the defect channel. A grammar with two
245
+ * homes drifts at the shapes neither author had in mind, so there is one home and it
246
+ * is `servableQueryFault`'s.
247
+ *
248
+ * Where the OFFENDING slice is THIS record's the ledger can name its key; where it
249
+ * belongs to an earlier hop it can only say so, a recorded hop keeping its query
250
+ * rather than the key it was composed from. That asymmetry is diagnostic-only and is
251
+ * why the message is built from a key that may be a placeholder.
252
+ */
253
+ export declare const assertServableAddition: (projections: Record<string, AnyProjection>) => Effect.Effect<void, TaglessSliceInLedger>;
254
+ /**
255
+ * The condition the ambient ledger derives right now, or `None` outside a bracket.
256
+ *
257
+ * This is what makes `buildDecisionModel` correct inside a bracket: the call whose
258
+ * current behaviour is the hazard derives from every hop the bracket walked rather
259
+ * than from its own single read.
260
+ */
261
+ export declare const ambientLayeredCondition: Effect.Effect<Option.Option<AppendCondition>>;
262
+ export {};
263
+ //# sourceMappingURL=readLedger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readLedger.d.ts","sourceRoot":"","sources":["../../src/readLedger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,KAAK,EACN,MAAM,iBAAiB,CAAA;AAOxB,OAAO,EAAE,OAAO,EAAQ,MAAM,EAAE,KAAK,EAAE,MAAM,EAAO,MAAM,QAAQ,CAAA;AAElE;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAA;CACzB;;IAuBG;;;;;;;;;;;;OAYG;qBACc,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;mBAC3C,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;;AAnCpD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,UAAW,SAAQ,eAmB7B;CAAG;AAEN;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;;;;AAE9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,eAAgB,SAAQ,qBACnC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CACrB;IACC;;;;;;;OAOG;IACH,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF;;;;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,oBAAqB,SAAQ,0BAExC;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;IAC9B,2DAA2D;IAC3D,IAAa,OAAO,IAAI,MAAM,CAO7B;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,aAAa,CAAC,GAAG,CAAC,KACvB,eAAe,GAAG,SAiBpB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAA;CACjD,CAsBC,CAAA;AAsBF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CACT,CAAA;AAqBxD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,KACzC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,oBAAoB,CAaxC,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,CACjD,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAK/B,CAAA"}
@@ -52,84 +52,52 @@
52
52
  * boundary, and decode may fail with `CodecError`. All surface on the error
53
53
  * channel, so a caller handles all three the same way — and
54
54
  * `@kairos-es/testing`'s `.thenError(tag)` can assert any of them.
55
+ *
56
+ * `decide` may also answer `Unchanged` — "already done, nothing to append" — but
57
+ * only where its type says so: `Decider`'s fifth type parameter `O` defaults to
58
+ * the non-empty batch, and `runDecider` returns `DecisionResult<O>`, which is
59
+ * plain `Position` unless `O` admits `Unchanged`. So a decider that cannot say
60
+ * "already done" keeps exactly the result type it always had — an untyped one
61
+ * and one built in a wrapper generic in its success type included — and one
62
+ * that can makes its caller handle `Position | Unchanged`. `runDecider` returns an
63
+ * `Unchanged` without calling `append`, so `append`'s refusal of an empty batch
64
+ * is untouched and never met. `decision.ts` owns why it is a success rather than
65
+ * an error or an empty batch, and why it is recognised by a type id.
55
66
  */
56
67
  import { DcbEventStore } from '@kairos-es/core';
57
68
  import { Effect } from 'effect';
69
+ import { isUnchanged } from "./decision.js";
58
70
  import { buildDecisionModel } from "./decisionModel.js";
59
- /**
60
- * Build a `Decider` with all four of its type arguments INFERRED: the slice
61
- * record `P` from what `projections` returns, the error union `E` and the extra
62
- * requirement `R` from what `decide` returns. The command type is the one thing
63
- * a caller still writes, once, as the annotation on the `projections` parameter
64
- * — `Command` occurs only in parameter positions, so nothing else could supply
65
- * it, and it is domain information (the command this decision is parametrised
66
- * by) rather than plumbing.
67
- *
68
- * It adds no capability: the interface stays directly inhabitable and this
69
- * returns exactly the object literal a caller would have written. What it
70
- * changes is what an author must SPELL. An object literal annotated
71
- * `Decider<P, Command, E>` has to state the whole slice record — a
72
- * `ReturnType<typeof someProjection>` per key, a slice's state type being
73
- * inferred by `projection().on().build()` and so having no name to write — plus
74
- * the union of every error its decide branches can fail with. Dropping the
75
- * annotation instead is not open to the author: without it `decide`'s `state`
76
- * and `command` parameters have no contextual type and are implicitly `any`. So
77
- * the cost landed hardest on the worked example, where some thirty lines of type
78
- * plumbing sat between the reader and the three decisions that file exists to
79
- * teach.
80
- *
81
- * `Eff` captures decide's WHOLE return type as one type parameter instead of
82
- * inferring `E` and `R` from an `Effect<…, E, Serializer | Uuid | R>` shaped
83
- * target, and that is the part that had to be got right rather than merely
84
- * written down. A real `decide` returns a UNION of effects — one `Effect.fail`
85
- * per rule it enforces, plus the produce path — and TypeScript infers a type
86
- * parameter from a union source by choosing one constituent's candidate, not by
87
- * unioning them. Against the shaped target `E` therefore came out as whichever
88
- * branch won, and every other branch was then rejected against it. Inference to
89
- * a NAKED type variable takes the union whole, which is why `Eff` is the
90
- * parameter and `Effect.Effect.Error` / `Effect.Effect.Context` recover the two
91
- * channels afterwards: those do distribute across a union of effects.
92
- *
93
- * The constraint on `Eff` is load-bearing twice over. It is what still refuses a
94
- * `decide` that could produce an EMPTY batch, the one guarantee the shaped
95
- * target used to give and the one `append` relies on. It is also what
96
- * contextually types the produce path's `(event) => [event]` as a one-element
97
- * TUPLE; unconstrained, that widens to `DcbEvent[]`, which is not a
98
- * `NonEmptyReadonlyArray` and would fail at the call site.
99
- *
100
- * The single `as` is narrow and unavoidable: the declared return is a
101
- * conditional over an unresolved `Eff`, so while `Eff` is still a type variable
102
- * the checker cannot relate `(…) => Eff` to `(…) => Effect<…,
103
- * Effect.Error<Eff>, …>`. The value is the two functions it was handed, so
104
- * there is nothing for the cast to be wrong about.
105
- *
106
- * `R` excludes `Serializer | Uuid` because that is what `R` MEANS on `Decider`:
107
- * the interface writes decide's requirement as `Serializer | Uuid | R`, so `R`
108
- * is what the produce side needs BEYOND the codec's own two services. Leaving
109
- * them in would state the same requirement twice and make an ordinary decider
110
- * read as though it carried an extra one, though the set `runDecider` ends up
111
- * asking for is identical either way. The exclusion cannot swallow a caller's
112
- * own service by accident: a `Context.Tag`'s requirement type carries its key as
113
- * a literal `Id` field, so only a tag declared under `@kairos-es/codec/Uuid` or
114
- * `@kairos-es/codec/Serializer` matches — and a tag under that key IS that
115
- * service, context being keyed by the same string at runtime.
116
- */
117
- export const decider = (projections, decide) => ({
118
- projections,
119
- decide
120
- });
71
+ export function decider(projections, decide) {
72
+ return {
73
+ projections,
74
+ decide
75
+ };
76
+ }
121
77
  /**
122
78
  * Run the full DCB command cycle for a decider against the ambient
123
79
  * `DcbEventStore`: build the decision model (real read + decode + fold), decide,
124
- * and append under the read's append condition. Returns the last new position.
80
+ * and append under the read's append condition. Returns the last new position —
81
+ * or, when `decide` answered `Unchanged`, that `Unchanged`, having appended
82
+ * nothing. The result type is `DecisionResult<O>`, so it is plain `Position` for
83
+ * every decider that cannot answer `Unchanged`, including one whose `O` is still
84
+ * a type variable bounded by the batch. ONE signature, deliberately:
85
+ * `decision.ts` says why that alias, and not an overload, is what carries it.
125
86
  *
126
87
  * The requirement is `DcbEventStore | Serializer | Uuid | R |
127
88
  * RequirementOf<P>`: `Serializer` enters through both the decode
128
89
  * (`buildDecisionModel`) and produce (`toEvent`) sides, `Uuid` through produce,
129
90
  * the slices' `RequirementOf<P>` through decode, and `R` through produce. (For a
130
91
  * def whose `Schema` carries a requirement, `RequirementOf<P>` and `R` are the
131
- * same tag.) Any `decide` failure, a decode `CodecError`, or an
132
- * `AppendConditionFailed` propagates on the error channel unchanged.
92
+ * same tag.) Any `decide` failure, a decode `CodecError`, an
93
+ * `AppendConditionFailed`, or the read's own `TaglessSliceInLedger` propagates on
94
+ * the error channel unchanged.
95
+ *
96
+ * ⚠ INSIDE a bracket this function is REFUSED, as a defect, at the store's shared
97
+ * append decorator, and `decideOnly` is what a body reaches for instead.
98
+ * `index.ts` states that pair, why each refuses exactly where the other is the
99
+ * answer, and why neither can be expressed over the other. Read it before
100
+ * changing either signature; this file does not restate the argument.
133
101
  */
134
102
  export const runDecider = (decider, command) => Effect.gen(function* () {
135
103
  const store = yield* DcbEventStore;
@@ -137,7 +105,14 @@ export const runDecider = (decider, command) => Effect.gen(function* () {
137
105
  state,
138
106
  appendCondition
139
107
  } = yield* buildDecisionModel(decider.projections(command));
140
- const newEvents = yield* decider.decide(state, command);
141
- return yield* store.append(newEvents, appendCondition);
108
+ const output = yield* decider.decide(state, command);
109
+ // "Already done" never reaches `append`: an empty batch stays a defect there,
110
+ // and this is not one. The two casts restate `DecisionResult<O>`, which the
111
+ // checker cannot relate to either branch while `O` is still a type variable;
112
+ // the branch taken is exactly the one the conditional selects.
113
+ if (isUnchanged(output)) {
114
+ return output;
115
+ }
116
+ return yield* store.append(output, appendCondition);
142
117
  });
143
118
  //# sourceMappingURL=decider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decider.js","names":["DcbEventStore","Effect","buildDecisionModel","decider","projections","decide","runDecider","command","gen","store","state","appendCondition","newEvents","append"],"sources":["../../src/decider.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,SAASA,aAAa,QAAQ,iBAAiB;AAE/C,SAASC,MAAM,QAAQ,QAAQ;AAC/B,SAASC,kBAAkB,QAA4B,oBAAiB;AAsCxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,OAAO,MAAMC,OAAO,GAAGA,CASrBC,WAAoC,EACpCC,MAA2D,MAO1D;EAAED,WAAW;EAAEC;AAAM,CAAE,CAKvB;AAEH;;;;;;;;;;;;;AAaA,OAAO,MAAMC,UAAU,GAAGA,CAMxBH,OAAkC,EAClCI,OAAgB,KAMhBN,MAAM,CAACO,GAAG,CAAC,aAAS;EAClB,MAAMC,KAAK,GAAG,OAAOT,aAAa;EAClC,MAAM;IAAEU,KAAK;IAAEC;EAAe,CAAE,GAAG,OAAOT,kBAAkB,CAC1DC,OAAO,CAACC,WAAW,CAACG,OAAO,CAAC,CAC7B;EACD,MAAMK,SAAS,GAAG,OAAOT,OAAO,CAACE,MAAM,CAACK,KAAK,EAAEH,OAAO,CAAC;EACvD,OAAO,OAAOE,KAAK,CAACI,MAAM,CAACD,SAAS,EAAED,eAAe,CAAC;AACxD,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"decider.js","names":["DcbEventStore","Effect","isUnchanged","buildDecisionModel","decider","projections","decide","runDecider","command","gen","store","state","appendCondition","output","append"],"sources":["../../src/decider.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,SAASA,aAAa,QAAQ,iBAAiB;AAE/C,SAASC,MAAM,QAAQ,QAAQ;AAC/B,SAGEC,WAAW,QACN,eAAY;AACnB,SAASC,kBAAkB,QAA4B,oBAAiB;AAoJxE,OAAM,SAAUC,OAAOA,CACrBC,WAAoC,EACpCC,MAGoD;EAEpD,OAAO;IAAED,WAAW;IAAEC;EAAM,CAM3B;AACH;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,OAAO,MAAMC,UAAU,GAAGA,CAOxBH,OAAqC,EACrCI,OAAgB,KAMhBP,MAAM,CAACQ,GAAG,CAAC,aAAS;EAClB,MAAMC,KAAK,GAAG,OAAOV,aAAa;EAClC,MAAM;IAAEW,KAAK;IAAEC;EAAe,CAAE,GAAG,OAAOT,kBAAkB,CAC1DC,OAAO,CAACC,WAAW,CAACG,OAAO,CAAC,CAC7B;EACD,MAAMK,MAAM,GAAmB,OAAOT,OAAO,CAACE,MAAM,CAACK,KAAK,EAAEH,OAAO,CAAC;EACpE;EACA;EACA;EACA;EACA,IAAIN,WAAW,CAACW,MAAM,CAAC,EAAE;IACvB,OAAOA,MAA2B;EACpC;EACA,OAAQ,OAAOH,KAAK,CAACI,MAAM,CAACD,MAAM,EAAED,eAAe,CAAC;AACtD,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,37 @@
1
+ import { Data, Predicate } from 'effect';
2
+ /**
3
+ * The type id `isUnchanged` recognises. `Symbol.for`, so both halves of the dual
4
+ * build — and any two copies of this package in one graph — agree on it.
5
+ */
6
+ export const UnchangedTypeId = /*#__PURE__*/Symbol.for('@kairos-es/codec/Unchanged');
7
+ /**
8
+ * "Nothing to do: this work is already done." A runner that receives it appends
9
+ * nothing and returns it in place of a `Position`.
10
+ *
11
+ * `reason` is for a human reading a trace or a test failure, and nothing in the
12
+ * library reads it. A `Data.TaggedClass`, so two values with the same reason are
13
+ * `Equal` and a test can compare them structurally.
14
+ *
15
+ * ⚠ Test for it with `isUnchanged`, never with `instanceof` or `_tag` — the
16
+ * module documentation says why each of those is wrong.
17
+ */
18
+ export class Unchanged extends /*#__PURE__*/Data.TaggedClass('Unchanged') {
19
+ [UnchangedTypeId] = UnchangedTypeId;
20
+ }
21
+ /**
22
+ * Whether `u` is an `Unchanged`, by its type id. Total over `unknown`, and
23
+ * realm-safe across the dual ESM/CJS build. An application value that merely
24
+ * carries `_tag: 'Unchanged'` answers `false`, deliberately.
25
+ */
26
+ export const isUnchanged = u => Predicate.hasProperty(u, UnchangedTypeId);
27
+ /**
28
+ * The constructors a `decide` step reaches for. One member today; it is a
29
+ * namespace object rather than a bare function so the call site reads as what it
30
+ * is, `Decision.unchanged('already dispatched')`.
31
+ */
32
+ export const Decision = {
33
+ unchanged: reason => new Unchanged(reason === undefined ? {} : {
34
+ reason
35
+ })
36
+ };
37
+ //# sourceMappingURL=decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.js","names":["Data","Predicate","UnchangedTypeId","Symbol","for","Unchanged","TaggedClass","isUnchanged","u","hasProperty","Decision","unchanged","reason","undefined"],"sources":["../../src/decision.ts"],"sourcesContent":[null],"mappings":"AA4FA,SAA4BA,IAAI,EAAEC,SAAS,QAAQ,QAAQ;AAE3D;;;;AAIA,OAAO,MAAMC,eAAe,gBAAkBC,MAAM,CAACC,GAAG,CACtD,4BAA4B,CAC7B;AAKD;;;;;;;;;;;AAWA,OAAM,MAAOC,SAAU,sBAAQL,IAAI,CAACM,WAAW,CAAC,WAAW,CAEzD;EACS,CAACJ,eAAe,IAAqBA,eAAe;;AAG/D;;;;;AAKA,OAAO,MAAMK,WAAW,GAAIC,CAAU,IACpCP,SAAS,CAACQ,WAAW,CAACD,CAAC,EAAEN,eAAe,CAAC;AAE3C;;;;;AAKA,OAAO,MAAMQ,QAAQ,GAEjB;EACFC,SAAS,EAAGC,MAAM,IAAK,IAAIP,SAAS,CAACO,MAAM,KAAKC,SAAS,GAAG,EAAE,GAAG;IAAED;EAAM,CAAE;CAC5E","ignoreList":[]}
@@ -7,11 +7,18 @@
7
7
  * `modelAtHead` stops where the fold does, at `{ state, head }` — a
8
8
  * materialisation of the record with no table, no checkpoint and no runner.
9
9
  * `buildDecisionModel` adds the only part that is genuinely decision-specific:
10
- * the `AppendCondition` derived from the same query and that same head (the DCB
11
- * optimistic-concurrency contract). It is core's old
12
- * `buildDecisionModel(Record<string, AnyProjection>)` surface, moved here
10
+ * the `AppendCondition` (the DCB optimistic-concurrency contract). It is core's
11
+ * old `buildDecisionModel(Record<string, AnyProjection>)` surface, moved here
13
12
  * because decode needs `Schema` + the `Serializer`.
14
13
  *
14
+ * BOTH exports also record into the ambient read ledger when one is in context,
15
+ * which is what makes a decision that walks a chain of identifiers guard on every
16
+ * hop rather than on its last. Recording is `modelAtHead`'s, so it happens
17
+ * whatever symbol performed the read; outside a bracket there is no ledger,
18
+ * nothing is recorded and no behaviour changes. `readLedger.ts` owns the whole
19
+ * argument, including why the recorder is this module rather than a
20
+ * layered-only entry point of its own.
21
+ *
15
22
  * The fold stays pure and in core; only the decode stage is effectful, and that
16
23
  * stage lives in `decode.ts` — `decodeSlices` — so the read side folds a live
17
24
  * `subscribe` stream through the *same* decode this module folds a bounded
@@ -25,8 +32,9 @@
25
32
  * binding this module owns (see `decode.ts` for why).
26
33
  */
27
34
  import { composeProjections, DcbEventStore } from '@kairos-es/core';
28
- import { Effect, Stream } from 'effect';
35
+ import { Effect, Option, Stream } from 'effect';
29
36
  import { decodeSlices } from "./decode.js";
37
+ import { ambientLayeredCondition, assertServableAddition, recordQuery } from "./readLedger.js";
30
38
  /**
31
39
  * Read the events for the composed slices' query, decode each through its def's
32
40
  * `Schema`, fold into per-key state, and return `{ state, head }`: one slice
@@ -51,6 +59,16 @@ import { decodeSlices } from "./decode.js";
51
59
  * single fold cannot serve — across many identities, ordered, joined, paginated
52
60
  * — and never because it is fresher, because it is not.
53
61
  *
62
+ * ⚠ AND INSIDE A BRACKET IT IS NOT THE FLOOR THE APPEND WILL USE. Everything
63
+ * above is a statement about THIS read, and it stays true; what changes is that a
64
+ * bracket guards on the EARLIEST head across every hop it walked, which on any hop
65
+ * after the first is a strictly lower number than the one returned here. So a
66
+ * caller who reads `head` inside a bracket and reasons about what the append will
67
+ * refuse is reasoning from the wrong figure — the guard is wider than this head
68
+ * says. Nothing here is wrong; the trap is assuming the two are the same number,
69
+ * and it is named because it is exactly the assumption that holds outside a
70
+ * bracket and quietly stops holding inside one. `readLedger.ts` owns the floor.
71
+ *
54
72
  * `head` is LOG-GLOBAL: it is the head the store's `read` returned, so it moves
55
73
  * for an event no slice here selects. `test/decisionModel.test.ts` pins that
56
74
  * behaviour EXPLICITLY, over four appended events one of which the composed query
@@ -82,12 +100,28 @@ export const modelAtHead = projections => Effect.gen(function* () {
82
100
  const decode = decodeSlices(projections);
83
101
  // The pure fold: core composes the record (slices are AnyProjections).
84
102
  const composite = composeProjections(projections);
103
+ // Both ledger calls below are no-ops outside a bracket and neither touches
104
+ // the requirement channel; `readLedger.ts` owns why recording happens HERE
105
+ // rather than at a layered-only entry point of its own.
106
+ //
107
+ // Refuse an unservable union BEFORE the read is paid for: the question is
108
+ // answerable from the queries alone, so a hop that would kill the append two
109
+ // layers away fails at the read that caused it instead.
110
+ yield* assertServableAddition(projections);
111
+ // EAGER: the query is filed before a single event is streamed, so a read that
112
+ // opens and never completes still widens the guard. What comes back is the
113
+ // effect that completes the hop; outside a bracket it does nothing.
114
+ const closeHop = yield* recordQuery(composite.query);
85
115
  const {
86
116
  events,
87
117
  head
88
118
  } = yield* store.read(composite.query);
89
119
  const decoded = decode(events);
90
120
  const state = yield* Stream.runFold(decoded, composite.initialState, (acc, event) => composite.evolve(acc, event));
121
+ // LAZY: only once the fold has run to completion. A read that failed
122
+ // part-way folded a PREFIX, and a head recorded for it is a claim it did not
123
+ // earn — so the line below is unreachable on that path, deliberately.
124
+ yield* closeHop(head);
91
125
  return {
92
126
  state,
93
127
  head
@@ -106,17 +140,19 @@ export const modelAtHead = projections => Effect.gen(function* () {
106
140
  * of a near-duplicate: `modelAtHead` is the shared part, this function is the
107
141
  * condition and nothing else, and each is shorter than the one function was.
108
142
  *
109
- * `composeProjections` therefore runs TWICE for one command — once inside
110
- * `modelAtHead` for the read, once here for `failIfEventsMatch` — and that is
111
- * deliberate rather than waste. It is a pure derivation of the record, so the two
112
- * queries cannot disagree however either call site changes. The alternative,
113
- * returning the query from `modelAtHead`, would put a third field on a READ-TIME
114
- * API that no read-time caller wants, to save one pure merge over a handful of
115
- * slices; a surface paying for a body's convenience is the worse trade.
143
+ * Outside a bracket `composeProjections` therefore runs twice for one command —
144
+ * once inside `modelAtHead` for the read, once here for `failIfEventsMatch` — and
145
+ * that is deliberate rather than waste. It is a pure derivation of the record, so
146
+ * the two queries cannot disagree however either call site changes. The
147
+ * alternative, returning the query from `modelAtHead`, would put a third field on
148
+ * a READ-TIME API that no read-time caller wants, to save one pure merge over a
149
+ * handful of slices; a surface paying for a body's convenience is the worse
150
+ * trade. Inside a bracket the second merge is not reached at all: the ledger's
151
+ * own derivation composes every recorded hop instead.
116
152
  *
117
153
  * ## How to attribute the read, the decode and the fold — the rule, stated once
118
154
  *
119
- * Because this function is `Effect.map` over `modelAtHead`, prose about the codec
155
+ * Because this function is expressed OVER `modelAtHead`, prose about the codec
120
156
  * splits into two kinds and only one of them may name this symbol:
121
157
  *
122
158
  * - Naming this function as the ENTRY POINT a caller invokes, or as the path
@@ -133,14 +169,34 @@ export const modelAtHead = projections => Effect.gen(function* () {
133
169
  * reads perfectly and is wrong. The correction is to name `modelAtHead`, never to
134
170
  * soften the sentence.
135
171
  */
136
- export const buildDecisionModel = projections => Effect.map(modelAtHead(projections), ({
137
- state,
138
- head
139
- }) => ({
140
- state,
141
- appendCondition: {
142
- failIfEventsMatch: composeProjections(projections).query,
143
- after: head
172
+ export const buildDecisionModel = projections => Effect.gen(function* () {
173
+ const {
174
+ state,
175
+ head
176
+ } = yield* modelAtHead(projections);
177
+ // INSIDE A BRACKET the condition comes from the ledger — every query the
178
+ // bracket walked, at the earliest head observed — and not from this one read.
179
+ // That is the change's whole ergonomic claim: the call whose current
180
+ // behaviour is the hazard becomes CORRECT inside a bracket, with nothing at
181
+ // the call site changing beyond entering one. The cost is that one expression
182
+ // means two things depending on ambient context, and that is the trade taken.
183
+ //
184
+ // `modelAtHead` has just recorded a hop, so inside a bracket the derivation
185
+ // cannot be empty; `None` here means there is no ledger at all.
186
+ const ambient = yield* ambientLayeredCondition;
187
+ if (Option.isSome(ambient)) {
188
+ return {
189
+ state,
190
+ appendCondition: ambient.value
191
+ };
144
192
  }
145
- }));
193
+ // OUTSIDE a bracket this is byte-for-byte what shipped.
194
+ return {
195
+ state,
196
+ appendCondition: {
197
+ failIfEventsMatch: composeProjections(projections).query,
198
+ after: head
199
+ }
200
+ };
201
+ });
146
202
  //# sourceMappingURL=decisionModel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decisionModel.js","names":["composeProjections","DcbEventStore","Effect","Stream","decodeSlices","modelAtHead","projections","gen","store","decode","composite","events","head","read","query","decoded","state","runFold","initialState","acc","event","evolve","buildDecisionModel","map","appendCondition","failIfEventsMatch","after"],"sources":["../../src/decisionModel.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAIEA,kBAAkB,EAClBC,aAAa,QAER,iBAAiB;AACxB,SAASC,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AACvC,SAASC,YAAY,QAAQ,aAAU;AASvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,OAAO,MAAMC,WAAW,GACtBC,WAAc,IAMdJ,MAAM,CAACK,GAAG,CAAC,aAAS;EAClB,MAAMC,KAAK,GAAG,OAAOP,aAAa;EAElC;EACA;EACA;EACA,MAAMQ,MAAM,GAAGL,YAAY,CAACE,WAAW,CAAC;EAExC;EACA,MAAMI,SAAS,GAAGV,kBAAkB,CAClCM,WAA4C,CAC7C;EACD,MAAM;IAAEK,MAAM;IAAEC;EAAI,CAAE,GAAG,OAAOJ,KAAK,CAACK,IAAI,CAACH,SAAS,CAACI,KAAK,CAAC;EAE3D,MAAMC,OAAO,GAAGN,MAAM,CAACE,MAAM,CAAC;EAE9B,MAAMK,KAAK,GAAI,OAAOb,MAAM,CAACc,OAAO,CAClCF,OAAO,EACPL,SAAS,CAACQ,YAAY,EACtB,CAACC,GAAG,EAAEC,KAAK,KAAKV,SAAS,CAACW,MAAM,CAACF,GAAG,EAAEC,KAAK,CAAC,CACvB;EAEvB,OAAO;IAAEJ,KAAK;IAAEJ;EAAI,CAAE;AACxB,CAAC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,OAAO,MAAMU,kBAAkB,GAC7BhB,WAAc,IASdJ,MAAM,CAACqB,GAAG,CAAClB,WAAW,CAACC,WAAW,CAAC,EAAE,CAAC;EAAEU,KAAK;EAAEJ;AAAI,CAAE,MAAM;EACzDI,KAAK;EACLQ,eAAe,EAAE;IACfC,iBAAiB,EAAEzB,kBAAkB,CACnCM,WAA4C,CAC7C,CAACQ,KAAK;IACPY,KAAK,EAAEd;;CAEV,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"decisionModel.js","names":["composeProjections","DcbEventStore","Effect","Option","Stream","decodeSlices","ambientLayeredCondition","assertServableAddition","recordQuery","modelAtHead","projections","gen","store","decode","composite","closeHop","query","events","head","read","decoded","state","runFold","initialState","acc","event","evolve","buildDecisionModel","ambient","isSome","appendCondition","value","failIfEventsMatch","after"],"sources":["../../src/decisionModel.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAIEA,kBAAkB,EAClBC,aAAa,QAER,iBAAiB;AACxB,SAASC,MAAM,EAAEC,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AAC/C,SAASC,YAAY,QAAQ,aAAU;AAGvC,SACEC,uBAAuB,EACvBC,sBAAsB,EACtBC,WAAW,QAEN,iBAAc;AAOrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,OAAO,MAAMC,WAAW,GACtBC,WAAc,IAMdR,MAAM,CAACS,GAAG,CAAC,aAAS;EAClB,MAAMC,KAAK,GAAG,OAAOX,aAAa;EAElC;EACA;EACA;EACA,MAAMY,MAAM,GAAGR,YAAY,CAACK,WAAW,CAAC;EAExC;EACA,MAAMI,SAAS,GAAGd,kBAAkB,CAClCU,WAA4C,CAC7C;EAED;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAOH,sBAAsB,CAACG,WAA4C,CAAC;EAE3E;EACA;EACA;EACA,MAAMK,QAAQ,GAAG,OAAOP,WAAW,CAACM,SAAS,CAACE,KAAK,CAAC;EAEpD,MAAM;IAAEC,MAAM;IAAEC;EAAI,CAAE,GAAG,OAAON,KAAK,CAACO,IAAI,CAACL,SAAS,CAACE,KAAK,CAAC;EAE3D,MAAMI,OAAO,GAAGP,MAAM,CAACI,MAAM,CAAC;EAE9B,MAAMI,KAAK,GAAI,OAAOjB,MAAM,CAACkB,OAAO,CAClCF,OAAO,EACPN,SAAS,CAACS,YAAY,EACtB,CAACC,GAAG,EAAEC,KAAK,KAAKX,SAAS,CAACY,MAAM,CAACF,GAAG,EAAEC,KAAK,CAAC,CACvB;EAEvB;EACA;EACA;EACA,OAAOV,QAAQ,CAACG,IAAI,CAAC;EAErB,OAAO;IAAEG,KAAK;IAAEH;EAAI,CAAE;AACxB,CAAC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,OAAO,MAAMS,kBAAkB,GAC7BjB,WAAc,IASdR,MAAM,CAACS,GAAG,CAAC,aAAS;EAClB,MAAM;IAAEU,KAAK;IAAEH;EAAI,CAAE,GAAG,OAAOT,WAAW,CAACC,WAAW,CAAC;EAEvD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAMkB,OAAO,GAAG,OAAOtB,uBAAuB;EAC9C,IAAIH,MAAM,CAAC0B,MAAM,CAACD,OAAO,CAAC,EAAE;IAC1B,OAAO;MAAEP,KAAK;MAAES,eAAe,EAAEF,OAAO,CAACG;IAAK,CAAE;EAClD;EAEA;EACA,OAAO;IACLV,KAAK;IACLS,eAAe,EAAE;MACfE,iBAAiB,EAAEhC,kBAAkB,CACnCU,WAA4C,CAC7C,CAACM,KAAK;MACPiB,KAAK,EAAEf;;GAEV;AACH,CAAC,CAAC","ignoreList":[]}