@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,191 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.unsafeAppend = exports.transact = exports.decideOnly = void 0;
7
+ var _core = require("@kairos-es/core");
8
+ var _effect = require("effect");
9
+ var _decision = require("./decision.js");
10
+ var _decisionModel = require("./decisionModel.js");
11
+ var _readLedger = require("./readLedger.js");
12
+ /**
13
+ * The DCB command cycle MINUS the append: read the decision model, decode, fold,
14
+ * decide, and RETURN the events for the bracket to append — or the decider's
15
+ * `Unchanged`, passed through as it came, for the bracket to append nothing.
16
+ *
17
+ * Bracket-only, and the refusal is STATIC — see the section below for the mechanism
18
+ * and, more importantly, for where the error actually lands, which is not where a
19
+ * reader first expects. Forced through anyway, being outside a bracket is a DEFECT
20
+ * rather than an error-channel value: it is not a data-dependent outcome a caller
21
+ * could handle, and the events produced would be dropped with nothing to append them.
22
+ *
23
+ * It is also what makes the nesting refusal liveable. A command runner inside a
24
+ * bracket is refused at the store, and this is the function a builder reaches for
25
+ * instead — one identifier away. A refusal is only affordable when the fix is that
26
+ * close.
27
+ *
28
+ * `modelAtHead` and NOT `buildDecisionModel`: this function must not derive a
29
+ * condition at all. The bracket owns that, and owning it in ONE place is what
30
+ * stops a caller appending under the last hop's condition alone.
31
+ *
32
+ * ## Bracket-only is a REQUIREMENT, so `tsc` refuses it rather than a run
33
+ *
34
+ * `ReadLedger` is named in the requirement channel and `transact` is the only thing
35
+ * that provides it, so a program that calls this outside a bracket cannot be RUN,
36
+ * and `tsc` is what says so — nothing has to execute for a caller to be told.
37
+ *
38
+ * ⚠ Measured, because the obvious stronger reading is wrong: the call site itself
39
+ * COMPILES. A bare `decideOnly(d, cmd)` outside a bracket is fine, and so is an
40
+ * exported helper over it; the requirement just propagates, as any unmet requirement
41
+ * does. What fails is the DISCHARGE — `Effect.provide`, `runPromise`, an annotation
42
+ * asking for `R = never` — possibly in another file. That is still a static refusal
43
+ * and it is the whole gain over the runtime defect this replaced. It is not a
44
+ * squiggle under the mistake.
45
+ *
46
+ * That is a PRESENCE check, and presence is the direction the type system can
47
+ * decide here — the same direction `transact`'s `Exclude<R, Id>` already spends on
48
+ * the precondition's output. ADR-0014's 2026-08-29 amendment carries the rule that
49
+ * follows: look for a presence check before reaching for a defect. This function
50
+ * shipped a defect first, and the check was available all along.
51
+ *
52
+ * ⚠ Two lines carry it and their JOBS differ. The RETURN ANNOTATION is the surface a
53
+ * consumer is judged against, and what the discharge above meets. The
54
+ * `yield* ReadLedger` keeps the requirement in the INFERRED type, so it survives the
55
+ * annotation being edited, and it dies at run time for the audience no type reaches
56
+ * — a JavaScript consumer runs no compile step — rather than handing back events
57
+ * with no bracket to append them. Nothing here READS the ledger; `modelAtHead` does
58
+ * the recording, ambiently, through `Effect.serviceOption`.
59
+ *
60
+ * ⚠ Do not try to write down which check catches which deletion. It was tried twice
61
+ * and was wrong twice, and `test/appendOwnership.test.ts` in the source repository
62
+ * says why the second failure means there should be no third attempt. Plant the
63
+ * mutation instead.
64
+ */
65
+ const decideOnly = (decider, command) => _effect.Effect.gen(function* () {
66
+ yield* _readLedger.ReadLedger;
67
+ const {
68
+ state
69
+ } = yield* (0, _decisionModel.modelAtHead)(decider.projections(command));
70
+ return yield* decider.decide(state, command);
71
+ });
72
+ /**
73
+ * The sanctioned exception to the nesting refusal: append these events under NO
74
+ * condition, and say in the identifier and in a required `reason` why that is
75
+ * deliberate.
76
+ *
77
+ * It covers three cases and no others: deliberate narrowing, a genuinely
78
+ * unconditional creation, and a second append inside one bracket. The `reason` is
79
+ * ceremony on purpose, and ADR-0014 decision 6 owns why a hatch has to cost more
80
+ * than the safe path — that argument is a DECISION and belongs to the record that
81
+ * took it, so it is not restated here. How biome turns a required argument into a
82
+ * review signal is the source repository's `biome-plugins/unsafeAppendReported.grit`.
83
+ *
84
+ * ⚠ It BYPASSES the derived condition rather than composing with it. A caller who
85
+ * genuinely needs *the union, minus one event type* must restate the whole
86
+ * condition here. If that turns up in practice, a composing override is the
87
+ * additive answer and this hatch is not it.
88
+ *
89
+ * `Effect.mapInputContext` over
90
+ * `Context.omit(AppendIsOwned)` is what lets it through the store's ownership
91
+ * refusal — the one route, and `AppendIsOwned.ts` owns why removing the marker is
92
+ * the shape rather than a second tag or a sanctioning flag.
93
+ */
94
+ exports.decideOnly = decideOnly;
95
+ const unsafeAppend = (events,
96
+ // Required by the signature and never read, which IS the mechanism: the value a
97
+ // caller has to write is what a reviewer sees in the diff, and `tsc` refuses the
98
+ // call that omits it. A lint rule asserting the same arity existed and was
99
+ // deleted, on the ground that the type already refuses it — which made that
100
+ // ground load-bearing, so `test/appendOwnership.test.ts` in the source repository
101
+ // pins it. Reading the value here would give it a second job and a second place to
102
+ // be wrong.
103
+ //
104
+ // NOT `_reason`, though it is unused. This name SHIPS — it is what a consumer's
105
+ // editor offers and what the published `.d.ts` carries — and a leading underscore
106
+ // is this ecosystem's way of saying "ignore me", which is the opposite of what a
107
+ // required argument whose whole purpose is to be read by a human should say.
108
+ // biome-ignore lint/correctness/noUnusedFunctionParameters: unread on purpose; this name ships in the .d.ts and an underscore would tell a consumer to ignore it
109
+ reason) => _effect.Effect.flatMap(_core.DcbEventStore, store =>
110
+ // ⛔ DO NOT SIMPLIFY THIS. The annotation reads as "map an empty context to an
111
+ // empty context", and the types say nothing else — but `Context.omit` runs
112
+ // against the RUNTIME context, which inside a bracket holds `AppendIsOwned`,
113
+ // and removing it is the entire reason this function can append at all. That
114
+ // marker is deliberately absent from every signature (it is read through
115
+ // `Effect.serviceOption`, which is what keeps it out of `append`'s requirement
116
+ // channel), so nothing here will fail to compile if this is deleted — what
117
+ // catches it is `test/appendOwnership.test.ts` in the source repository, not the
118
+ // type checker.
119
+ _effect.Effect.mapInputContext(store.append(events), context => _effect.Context.omit(_core.AppendIsOwned)(context)));
120
+ /**
121
+ * Open a bracket over ONE decision: evaluate the precondition inside it, hand its
122
+ * output to the body under the precondition's tag, and append what the body
123
+ * produced under the union of every read either of them performed.
124
+ *
125
+ * Curried so the precondition reads as the thing the command DECLARES and the body
126
+ * reads as the thing it DOES.
127
+ *
128
+ * The ownership marker is provided ALONGSIDE the ledger and to the body only, so
129
+ * any append inside the body — a nested bracket, a command runner, a raw
130
+ * `store.append` — is refused at the store as a defect, while the bracket's own
131
+ * closing append below runs outside that scope and is unaffected. That is why the
132
+ * refusal is on APPENDING rather than on opening a bracket: a command runner
133
+ * appends without opening one, and its events usually match the union the bracket
134
+ * is about to guard on, so the bracket's own close would be refused by a condition
135
+ * its own body invalidated.
136
+ *
137
+ * The body may succeed with `Unchanged` (see the module documentation), and the
138
+ * result is then `DecisionResult<O>`: `Position` for a body that cannot answer
139
+ * it, `Position | Unchanged` for one that can. `O` is last and defaulted, so an
140
+ * explicit `<E, R>` written against the narrower signature still means what it
141
+ * did.
142
+ */
143
+ exports.unsafeAppend = unsafeAppend;
144
+ const transact = precondition => body => _effect.Effect.gen(function* () {
145
+ const store = yield* _core.DcbEventStore;
146
+ const ledger = yield* _readLedger.makeReadLedger;
147
+ const output = yield* _effect.Effect.gen(function* () {
148
+ // ORDER IS THE DESIGN. The precondition runs first and runs HERE, with the
149
+ // ledger already in context, so its reads are hops in the very condition
150
+ // that will guard the body's write.
151
+ const proof = yield* precondition.evaluate;
152
+ return yield* body.pipe(_effect.Effect.provideService(precondition.tag, proof));
153
+ }).pipe(
154
+ // ONE layer, not two provisions, and it is the ANNOTATION above that makes
155
+ // that mandatory rather than tidy.
156
+ //
157
+ // TypeScript does not simplify a deferred conditional over an unresolved
158
+ // type variable, so `Exclude<Exclude<X, A>, B>` and
159
+ // `Exclude<X, A | B>` are unrelated types while `X` is still a parameter,
160
+ // and every provision here adds one level of nesting the annotation has to
161
+ // mirror EXACTLY. Both halves measured, each as a `TS2719` at the return
162
+ // annotation: two chained provisions refuse the flat
163
+ // `Exclude<PR, ReadLedger | AppendIsOwned>`, and even with this merge the
164
+ // flat `Exclude<R, Id | ReadLedger | AppendIsOwned>` is refused too,
165
+ // because the precondition's own tag is provided separately and further in.
166
+ // So `PR` is flat and `R` is one deep, and that asymmetry is the pipeline
167
+ // showing through rather than a slip.
168
+ //
169
+ // Merging says what is true anyway, which is why it is the fix rather than
170
+ // a cast: the ledger and the ownership marker have one lifetime and one
171
+ // meaning between them, and that meaning is *you are inside a bracket*.
172
+ _effect.Effect.provide(_effect.Layer.merge(ledger.layer, _effect.Layer.succeed(_core.AppendIsOwned, {
173
+ by: 'transact'
174
+ }))));
175
+ // "Already done": nothing to append, so no condition to derive and nothing
176
+ // for `NoReadsRecorded` to protect — that refusal guards a WRITE that would
177
+ // go out unguarded, and there is none. It must stay BEFORE the derivation,
178
+ // or an empty bracket answering `Unchanged` would be refused for a write it
179
+ // never asked for. The casts restate `DecisionResult<O>`, which the checker
180
+ // cannot relate to either branch while `O` is still a type variable.
181
+ if ((0, _decision.isUnchanged)(output)) {
182
+ return output;
183
+ }
184
+ const condition = (0, _readLedger.deriveLayeredCondition)(yield* ledger.hops);
185
+ if (condition === undefined) {
186
+ return yield* new _readLedger.NoReadsRecorded();
187
+ }
188
+ return yield* store.append(output, condition);
189
+ });
190
+ exports.transact = transact;
191
+ //# sourceMappingURL=precondition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precondition.js","names":["_core","require","_effect","_decision","_decisionModel","_readLedger","decideOnly","decider","command","Effect","gen","ReadLedger","state","modelAtHead","projections","decide","exports","unsafeAppend","events","reason","flatMap","DcbEventStore","store","mapInputContext","append","context","Context","omit","AppendIsOwned","transact","precondition","body","ledger","makeReadLedger","output","proof","evaluate","pipe","provideService","tag","provide","Layer","merge","layer","succeed","by","isUnchanged","condition","deriveLayeredCondition","hops","undefined","NoReadsRecorded"],"sources":["../../src/precondition.ts"],"sourcesContent":[null],"mappings":";;;;;;AAwGA,IAAAA,KAAA,GAAAC,OAAA;AAEA,IAAAC,OAAA,GAAAD,OAAA;AAEA,IAAAE,SAAA,GAAAF,OAAA;AAKA,IAAAG,cAAA,GAAAH,OAAA;AAGA,IAAAI,WAAA,GAAAJ,OAAA;AAsCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDO,MAAMK,UAAU,GAAGA,CAOxBC,OAAqC,EACrCC,OAAgB,KAMhBC,cAAM,CAACC,GAAG,CAAC,aAAS;EAClB,OAAOC,sBAAU;EAEjB,MAAM;IAAEC;EAAK,CAAE,GAAG,OAAO,IAAAC,0BAAW,EAACN,OAAO,CAACO,WAAW,CAACN,OAAO,CAAC,CAAC;EAClE,OAAO,OAAOD,OAAO,CAACQ,MAAM,CAACH,KAAK,EAAEJ,OAAO,CAAC;AAC9C,CAAC,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;AAAAQ,OAAA,CAAAV,UAAA,GAAAA,UAAA;AAsBO,MAAMW,YAAY,GAAGA,CAC1BC,MAA2C;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACAC,MAAc,KAEdV,cAAM,CAACW,OAAO,CAACC,mBAAa,EAAGC,KAAK;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACAb,cAAM,CAACc,eAAe,CACpBD,KAAK,CAACE,MAAM,CAACN,MAAM,CAAC,EACnBO,OAA+B,IAAKC,eAAO,CAACC,IAAI,CAACC,mBAAa,CAAC,CAACH,OAAO,CAAC,CAC1E,CACF;AAEH;;;;;;;;;;;;;;;;;;;;;;;AAAAT,OAAA,CAAAC,YAAA,GAAAA,YAAA;AAuBO,MAAMY,QAAQ,GACGC,YAA+C,IAEnEC,IAA4B,IAQ5BtB,cAAM,CAACC,GAAG,CAAC,aAAS;EAClB,MAAMY,KAAK,GAAG,OAAOD,mBAAa;EAClC,MAAMW,MAAM,GAAG,OAAOC,0BAAc;EAEpC,MAAMC,MAAM,GAAmB,OAAOzB,cAAM,CAACC,GAAG,CAAC,aAAS;IACxD;IACA;IACA;IACA,MAAMyB,KAAK,GAAG,OAAOL,YAAY,CAACM,QAAQ;IAC1C,OAAO,OAAOL,IAAI,CAACM,IAAI,CAAC5B,cAAM,CAAC6B,cAAc,CAACR,YAAY,CAACS,GAAG,EAAEJ,KAAK,CAAC,CAAC;EACzE,CAAC,CAAC,CAACE,IAAI;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA5B,cAAM,CAAC+B,OAAO,CACZC,aAAK,CAACC,KAAK,CACTV,MAAM,CAACW,KAAK,EACZF,aAAK,CAACG,OAAO,CAAChB,mBAAa,EAAE;IAAEiB,EAAE,EAAE;EAAU,CAAE,CAAC,CACjD,CACF,CACF;EAED;EACA;EACA;EACA;EACA;EACA;EACA,IAAI,IAAAC,qBAAW,EAACZ,MAAM,CAAC,EAAE;IACvB,OAAOA,MAA2B;EACpC;EAEA,MAAMa,SAAS,GAAG,IAAAC,kCAAsB,EAAC,OAAOhB,MAAM,CAACiB,IAAI,CAAC;EAC5D,IAAIF,SAAS,KAAKG,SAAS,EAAE;IAC3B,OAAO,OAAO,IAAIC,2BAAe,EAAE;EACrC;EAEA,OAAQ,OAAO7B,KAAK,CAACE,MAAM,CAACU,MAAM,EAAEa,SAAS,CAAC;AAChD,CAAC,CAAC;AAAA/B,OAAA,CAAAa,QAAA,GAAAA,QAAA","ignoreList":[]}
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.recordQuery = exports.makeReadLedger = exports.deriveLayeredCondition = exports.assertServableAddition = exports.ambientLayeredCondition = exports.TaglessSliceInLedger = exports.ReadLedger = exports.NoReadsRecorded = void 0;
7
+ var _core = require("@kairos-es/core");
8
+ var _effect = require("effect");
9
+ /**
10
+ * The ambient ledger, present only inside a bracket.
11
+ *
12
+ * ⚠ A RECORDING path reads it with `Effect.serviceOption` and never
13
+ * `yield* ReadLedger`: its absence is the normal case there, and costing the
14
+ * requirement channel nothing is exactly what makes this change additive.
15
+ * `modelAtHead` is that path and is why the rule exists.
16
+ *
17
+ * A BRACKET-ONLY function is the opposite case and yields the tag deliberately.
18
+ * `decideOnly` does, and naming the requirement is what turns *"call me inside a
19
+ * bracket"* from a runtime defect into a compile error — presence being the one
20
+ * direction the type system can decide here. So the test is what the function
21
+ * means outside a bracket: nothing (record ambiently), or nothing sensible
22
+ * (require it).
23
+ *
24
+ * Recording is two-phase, and `record`'s own doc below owns why and what shape the
25
+ * second phase takes.
26
+ */
27
+ class ReadLedger extends /*#__PURE__*/_effect.Context.Tag('@kairos-es/codec/ReadLedger')() {}
28
+ /**
29
+ * Raised when a bracket closes having recorded no read at all.
30
+ *
31
+ * On the ERROR channel and deliberately split from the nested-append DEFECT,
32
+ * because the two are different mistakes. An empty bracket can arise from a
33
+ * data-dependent branch that produces events without reading — a genuinely
34
+ * unconditional creation, written inside a bracket by mistake — where a nested
35
+ * append is always a wiring fault. An error-channel value lets a caller decide
36
+ * which it is.
37
+ *
38
+ * Its job is narrower than it looks, and the narrowing is worth stating because
39
+ * the wider reading is the tempting one. Once a bracket's guard is itself a fold,
40
+ * a body that reads nothing still has a hop, so this cannot fire — it is the guard
41
+ * against an unguarded UNCONDITIONAL bracket, not against an unguarded bracket. A
42
+ * bracket that looks guarded and is not is NOT what this catches and never was,
43
+ * and nothing else catches it either — `precondition.ts` owns that gap and the
44
+ * rule a reader has to keep in its place. A refusal taken BEFORE reading is not
45
+ * this case either: it short-circuits and surfaces as the refusal.
46
+ */
47
+ exports.ReadLedger = ReadLedger;
48
+ class NoReadsRecorded extends /*#__PURE__*/_effect.Data.TaggedError('NoReadsRecorded') {
49
+ /**
50
+ * The sentence, as the `Error` field that says why — so the fault renders
51
+ * ITSELF, exactly as this package's other two errors do (`errors.ts` owns the
52
+ * argument for the mechanism and it is not restated here).
53
+ *
54
+ * A getter over a constant rather than a constructor field, because there is no
55
+ * datum to vary: every raise of this error means one thing.
56
+ */
57
+ get message() {
58
+ return 'the bracket closed having recorded no read, so there is no append condition ' + 'to derive. A decision that reads nothing is unconditional — say so with the ' + 'escape hatch, which takes a reason, rather than by opening a bracket.';
59
+ }
60
+ }
61
+ /**
62
+ * Raised when recording a hop would leave the bracket's accumulated union
63
+ * unservable.
64
+ *
65
+ * The store refuses a multi-item query in which any item is tagless, and it
66
+ * refuses it by THROWING inside the append decorator, which becomes a defect:
67
+ * uncatchable, one call and one layer away from the `build()` that chose no tags.
68
+ * A slice built without tags defaults to none, so it is perfectly servable alone
69
+ * and kills the append the moment it is unioned. This moves that failure onto the
70
+ * error channel, to the read that caused it, naming the record entry to fix.
71
+ *
72
+ * It refuses an unservable UNION and not every tagless hop, which is the easy
73
+ * mistake to make in the other direction: one tagless slice read on its own is
74
+ * servable and must keep working. Two cases reach it — a tagless hop unioned with
75
+ * another, and a single hop whose own RECORD holds two tagless slices, which
76
+ * composes to a multi-item query with no second hop needed.
77
+ */
78
+ exports.NoReadsRecorded = NoReadsRecorded;
79
+ class TaglessSliceInLedger extends /*#__PURE__*/_effect.Data.TaggedError('TaglessSliceInLedger') {
80
+ /** The offending slice and the remedy, in one sentence. */
81
+ get message() {
82
+ return `slice '${this.sliceKey}' was built with no tags, and this bracket now reads ` + 'more than one slice. A layered append condition unions every query it ' + 'recorded, and the store refuses a multi-item query if any item is tagless. ' + 'Pass `build({ tags: [...] })` for this slice.';
83
+ }
84
+ }
85
+ /**
86
+ * The ONE derivation: the union of every query walked, at the earliest head
87
+ * observed. Package-internal, for the reason the module doc gives.
88
+ *
89
+ * The union goes through core's own `unionQueries` — the merge
90
+ * `composeProjections` folds a record through, reached without one — and never a
91
+ * spread of `items`, because a wildcard hop (`items: []`) is ABSORBING under OR
92
+ * while a spread of an empty list contributes nothing, so hand-rolling the union
93
+ * narrows a match-everything hop to its sibling, silently and in the unsafe
94
+ * direction. A
95
+ * repeated or overlapping hop is harmless because this derives ONCE from a merged
96
+ * set and matching is idempotent over an OR; that property belongs to deriving
97
+ * once and would be lost by a refactor to per-hop evaluation.
98
+ *
99
+ * ## When any hop is headless the floor is `ORIGIN`, and the alternative is unsound
100
+ *
101
+ * The earliest head that actually ARRIVED is refused, and is named here as refused
102
+ * rather than merely not chosen, because it is what a reader writes by accident
103
+ * when told to take the earliest head. With one headless hop among completed hops
104
+ * the union looks right and the floor betrays it, sitting already above the
105
+ * invalidating event.
106
+ *
107
+ * `ORIGIN`'s cost is measured rather than described: it refuses every append to
108
+ * any identity that already has history, so it is not a conservative default but a
109
+ * hard stop. It is affordable only because conflict retry wraps the bracket from
110
+ * OUTSIDE and re-reads, which is the rule the whole shape depends on.
111
+ */
112
+ exports.TaglessSliceInLedger = TaglessSliceInLedger;
113
+ const deriveLayeredCondition = hops => {
114
+ if (hops.length === 0) {
115
+ return undefined;
116
+ }
117
+ const merged = (0, _core.unionQueries)(hops.map(hop => hop.query));
118
+ const arrived = hops.flatMap(hop => hop.head === undefined ? [] : [hop.head]);
119
+ // A headless hop widens the guard to the whole log. Unioning its query is the
120
+ // half that is not in dispute; the floor is where the two readings diverge.
121
+ const after = arrived.length === hops.length ? arrived.reduce((a, b) => b < a ? b : a) : _core.ORIGIN;
122
+ return {
123
+ failIfEventsMatch: merged,
124
+ after
125
+ };
126
+ };
127
+ /**
128
+ * A fresh ledger and the `Layer` providing it: a `Ref` of hops behind the two-phase
129
+ * `record` the service declares.
130
+ *
131
+ * The hops effect is handed back beside the layer because the bracket reads them
132
+ * AFTER the body has run and the layer is out of scope, which is the same moment it
133
+ * stops being able to reach the service.
134
+ */
135
+ exports.deriveLayeredCondition = deriveLayeredCondition;
136
+ const makeReadLedger = exports.makeReadLedger = /*#__PURE__*/_effect.Effect.gen(function* () {
137
+ const ref = yield* _effect.Ref.make([]);
138
+ return {
139
+ layer: _effect.Layer.succeed(ReadLedger, {
140
+ record: query => _effect.Ref.modify(ref, hops => [
141
+ // The close effect closes over the slot this hop just took, so the slot
142
+ // never becomes a value anything else has to carry or interpret.
143
+ head => _effect.Ref.update(ref, current => current.map((hop, index) => index === hops.length ? {
144
+ query: hop.query,
145
+ head
146
+ } : hop)), [...hops, {
147
+ query
148
+ }]]),
149
+ hops: _effect.Ref.get(ref)
150
+ }),
151
+ // The same effect the service exposes, reachable AFTER the layer is out of
152
+ // scope — which is exactly when the bracket derives.
153
+ hops: _effect.Ref.get(ref)
154
+ };
155
+ });
156
+ /**
157
+ * Run `f` against the ambient ledger, or answer `absent` when there is none.
158
+ *
159
+ * "Outside a bracket this is a no-op" is ONE fact stated once, rather than one
160
+ * per consumer free to drift from the others. Every ambient lookup in this module
161
+ * goes through here, which is also what keeps `Effect.serviceOption` — the reason
162
+ * nothing enters the requirement channel — from being a detail each caller has to
163
+ * remember.
164
+ */
165
+ const withLedger = (absent, f) => _effect.Effect.flatMap(_effect.Effect.serviceOption(ReadLedger), ledger => _effect.Option.isNone(ledger) ? _effect.Effect.succeed(absent) : f(ledger.value));
166
+ /** A close effect that does nothing: what a read outside a bracket gets back. */
167
+ const NO_CLOSE = () => _effect.Effect.void;
168
+ /**
169
+ * File a query into the ambient ledger if there is one, and hand back the effect
170
+ * that completes it. Outside a bracket the whole thing is a no-op, and the caller
171
+ * still writes one line rather than a conditional.
172
+ */
173
+ const recordQuery = query => withLedger(NO_CLOSE, ledger => ledger.record(query));
174
+ /** The tagless slice keys in a record, in declaration order. */
175
+ exports.recordQuery = recordQuery;
176
+ const taglessKeys = projections => Object.entries(projections).filter(([, projection]) => projection.query.items.some(item => item.tags.length === 0)).map(([key]) => key);
177
+ /** Would recording this record's query leave the accumulated union unservable? */
178
+ const unionWouldBeUnservable = (existing, incoming) => (0, _core.servableQueryFault)((0, _core.unionQueries)([...existing.map(h => h.query), incoming])) !== undefined;
179
+ /**
180
+ * Refuse a read that would leave the bracket's accumulated union unservable,
181
+ * BEFORE the read is paid for.
182
+ *
183
+ * Servability is answerable from the queries alone, so the check runs when a hop is
184
+ * recorded rather than at append time — the eager half of the eager/lazy split doing
185
+ * one more job. Outside a bracket nothing is unioned and nothing is unservable, so
186
+ * the ordinary single-hop caller is untaxed.
187
+ *
188
+ * It judges the PROSPECTIVE UNION and asks core for the verdict, which is what makes
189
+ * both failing shapes one case rather than two. An earlier version tested each
190
+ * recorded query separately and treated a FIRST hop as unconditionally fine — but a
191
+ * hop is a RECORD, and a record of two tagless slices composes to a multi-item query
192
+ * that is unservable on its own. That read died at the store, uncatchably, on the
193
+ * exact path this error exists to keep off the defect channel. A grammar with two
194
+ * homes drifts at the shapes neither author had in mind, so there is one home and it
195
+ * is `servableQueryFault`'s.
196
+ *
197
+ * Where the OFFENDING slice is THIS record's the ledger can name its key; where it
198
+ * belongs to an earlier hop it can only say so, a recorded hop keeping its query
199
+ * rather than the key it was composed from. That asymmetry is diagnostic-only and is
200
+ * why the message is built from a key that may be a placeholder.
201
+ */
202
+ const assertServableAddition = projections => withLedger(undefined, ledger => _effect.Effect.flatMap(ledger.hops, existing => {
203
+ const incoming = (0, _core.composeProjections)(projections).query;
204
+ if (!unionWouldBeUnservable(existing, incoming)) {
205
+ return _effect.Effect.void;
206
+ }
207
+ // The offending key where the tagless slice is THIS record's, and a
208
+ // placeholder where it belongs to an earlier hop — a recorded hop keeps its
209
+ // query rather than the key it was composed from.
210
+ const offending = taglessKeys(projections)[0] ?? '<an earlier hop>';
211
+ return new TaglessSliceInLedger({
212
+ sliceKey: offending
213
+ });
214
+ }));
215
+ /**
216
+ * The condition the ambient ledger derives right now, or `None` outside a bracket.
217
+ *
218
+ * This is what makes `buildDecisionModel` correct inside a bracket: the call whose
219
+ * current behaviour is the hazard derives from every hop the bracket walked rather
220
+ * than from its own single read.
221
+ */
222
+ exports.assertServableAddition = assertServableAddition;
223
+ const ambientLayeredCondition = exports.ambientLayeredCondition = /*#__PURE__*/withLedger(/*#__PURE__*/_effect.Option.none(), ledger => _effect.Effect.map(ledger.hops, hops => _effect.Option.fromNullable(deriveLayeredCondition(hops))));
224
+ //# sourceMappingURL=readLedger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readLedger.js","names":["_core","require","_effect","ReadLedger","Context","Tag","exports","NoReadsRecorded","Data","TaggedError","message","TaglessSliceInLedger","sliceKey","deriveLayeredCondition","hops","length","undefined","merged","unionQueries","map","hop","query","arrived","flatMap","head","after","reduce","a","b","ORIGIN","failIfEventsMatch","makeReadLedger","Effect","gen","ref","Ref","make","layer","Layer","succeed","record","modify","update","current","index","get","withLedger","absent","f","serviceOption","ledger","Option","isNone","value","NO_CLOSE","void","recordQuery","taglessKeys","projections","Object","entries","filter","projection","items","some","item","tags","key","unionWouldBeUnservable","existing","incoming","servableQueryFault","h","assertServableAddition","composeProjections","offending","ambientLayeredCondition","none","fromNullable"],"sources":["../../src/readLedger.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -52,14 +52,27 @@
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
- import type { AppendConditionFailed, CompositeState, DcbEvent, Position } from '@kairos-es/core';
67
+ import type { AppendConditionFailed, CompositeState, DcbEvent } from '@kairos-es/core';
57
68
  import { DcbEventStore } from '@kairos-es/core';
58
69
  import type { Array as Arr } from 'effect';
59
70
  import { Effect } from 'effect';
71
+ import { type DecisionOutput, type DecisionResult } from './decision.js';
60
72
  import { type RequirementOf } from './decisionModel.js';
61
73
  import type { CodecError } from './errors.js';
62
74
  import type { SliceProjection } from './projection.js';
75
+ import type { TaglessSliceInLedger } from './readLedger.js';
63
76
  import type { Serializer } from './Serializer.js';
64
77
  import type { Uuid } from './Uuid.js';
65
78
  /**
@@ -76,10 +89,17 @@ import type { Uuid } from './Uuid.js';
76
89
  * schema-backed, state-erased bound for a heterogeneous record); the
77
90
  * precise per-key state is recovered by core's shared `CompositeState`. The
78
91
  * requirement `R` is the union carried by the record's slices' schemas.
92
+ *
93
+ * `O` is what `decide` may succeed with. It defaults to the non-empty batch, and
94
+ * a decider that can answer "already done" widens it to `DecisionOutput`
95
+ * (`NonEmptyReadonlyArray<DcbEvent> | Unchanged`). It is LAST and DEFAULTED so
96
+ * that every `Decider<P, Command, E, R>` already written means exactly what it
97
+ * meant. Its bound is `DecisionOutput`, which still excludes a possibly-empty
98
+ * array — the widening adds an answer and removes no guarantee.
79
99
  */
80
- export interface Decider<P extends Record<string, SliceProjection>, Command, E = never, R = never> {
100
+ export interface Decider<P extends Record<string, SliceProjection>, Command, E = never, R = never, O extends DecisionOutput = Arr.NonEmptyReadonlyArray<DcbEvent>> {
81
101
  readonly projections: (command: Command) => P;
82
- readonly decide: (state: CompositeState<P>, command: Command) => Effect.Effect<Arr.NonEmptyReadonlyArray<DcbEvent>, E, Serializer | Uuid | R>;
102
+ readonly decide: (state: CompositeState<P>, command: Command) => Effect.Effect<O, E, Serializer | Uuid | R>;
83
103
  }
84
104
  /**
85
105
  * Build a `Decider` with all four of its type arguments INFERRED: the slice
@@ -128,6 +148,22 @@ export interface Decider<P extends Record<string, SliceProjection>, Command, E =
128
148
  * Effect.Error<Eff>, …>`. The value is the two functions it was handed, so
129
149
  * there is nothing for the cast to be wrong about.
130
150
  *
151
+ * The success channel is how a decider opts into `Unchanged` without writing a
152
+ * fifth type argument, and it is why this function is declared TWICE. The first
153
+ * declaration bounds `Eff` by the non-empty batch and returns a `Decider` with
154
+ * the DEFAULT `O`; the second bounds it by `DecisionOutput` and returns one whose
155
+ * `O` is `DecisionOutput`. A `decide` with a branch returning
156
+ * `Decision.unchanged(…)` fails the first bound and lands on the second, and one
157
+ * with no such branch keeps the default. Either way `O` is one of the two a
158
+ * caller ever writes, never the inferred success type passed through: inference
159
+ * is more precise than anyone writes — the produce path's one-element tuple, say
160
+ * — and a `Decider<…, readonly [DcbEvent]>` would not be the
161
+ * `Decider<P, Command, E, R>` its caller spells, though it behaves identically.
162
+ * Asking by ASSIGNABILITY rather than by a conditional over `Eff`'s success is
163
+ * what keeps an untyped (`any`) success, and a success that is a type variable
164
+ * bounded by the batch, on the default; `decision.ts` owns that argument, and
165
+ * the one type query the pair cannot answer as before.
166
+ *
131
167
  * `R` excludes `Serializer | Uuid` because that is what `R` MEANS on `Decider`:
132
168
  * the interface writes decide's requirement as `Serializer | Uuid | R`, so `R`
133
169
  * is what the produce side needs BEYOND the codec's own two services. Leaving
@@ -139,19 +175,32 @@ export interface Decider<P extends Record<string, SliceProjection>, Command, E =
139
175
  * `@kairos-es/codec/Serializer` matches — and a tag under that key IS that
140
176
  * service, context being keyed by the same string at runtime.
141
177
  */
142
- export declare const decider: <P extends Record<string, SliceProjection>, Command, Eff extends Effect.Effect<Arr.NonEmptyReadonlyArray<DcbEvent>, unknown, unknown>>(projections: (command: Command) => P, decide: (state: CompositeState<P>, command: Command) => Eff) => Decider<P, Command, Effect.Effect.Error<Eff>, Exclude<Effect.Effect.Context<Eff>, Serializer | Uuid>>;
178
+ export declare function decider<P extends Record<string, SliceProjection>, Command, Eff extends Effect.Effect<Arr.NonEmptyReadonlyArray<DcbEvent>, unknown, unknown>>(projections: (command: Command) => P, decide: (state: CompositeState<P>, command: Command) => Eff): Decider<P, Command, Effect.Effect.Error<Eff>, Exclude<Effect.Effect.Context<Eff>, Serializer | Uuid>>;
179
+ export declare function decider<P extends Record<string, SliceProjection>, Command, Eff extends Effect.Effect<DecisionOutput, unknown, unknown>>(projections: (command: Command) => P, decide: (state: CompositeState<P>, command: Command) => Eff): Decider<P, Command, Effect.Effect.Error<Eff>, Exclude<Effect.Effect.Context<Eff>, Serializer | Uuid>, DecisionOutput>;
143
180
  /**
144
181
  * Run the full DCB command cycle for a decider against the ambient
145
182
  * `DcbEventStore`: build the decision model (real read + decode + fold), decide,
146
- * and append under the read's append condition. Returns the last new position.
183
+ * and append under the read's append condition. Returns the last new position —
184
+ * or, when `decide` answered `Unchanged`, that `Unchanged`, having appended
185
+ * nothing. The result type is `DecisionResult<O>`, so it is plain `Position` for
186
+ * every decider that cannot answer `Unchanged`, including one whose `O` is still
187
+ * a type variable bounded by the batch. ONE signature, deliberately:
188
+ * `decision.ts` says why that alias, and not an overload, is what carries it.
147
189
  *
148
190
  * The requirement is `DcbEventStore | Serializer | Uuid | R |
149
191
  * RequirementOf<P>`: `Serializer` enters through both the decode
150
192
  * (`buildDecisionModel`) and produce (`toEvent`) sides, `Uuid` through produce,
151
193
  * the slices' `RequirementOf<P>` through decode, and `R` through produce. (For a
152
194
  * def whose `Schema` carries a requirement, `RequirementOf<P>` and `R` are the
153
- * same tag.) Any `decide` failure, a decode `CodecError`, or an
154
- * `AppendConditionFailed` propagates on the error channel unchanged.
195
+ * same tag.) Any `decide` failure, a decode `CodecError`, an
196
+ * `AppendConditionFailed`, or the read's own `TaglessSliceInLedger` propagates on
197
+ * the error channel unchanged.
198
+ *
199
+ * ⚠ INSIDE a bracket this function is REFUSED, as a defect, at the store's shared
200
+ * append decorator, and `decideOnly` is what a body reaches for instead.
201
+ * `index.ts` states that pair, why each refuses exactly where the other is the
202
+ * answer, and why neither can be expressed over the other. Read it before
203
+ * changing either signature; this file does not restate the argument.
155
204
  */
156
- export declare const runDecider: <P extends Record<string, SliceProjection>, Command, E, R>(decider: Decider<P, Command, E, R>, command: Command) => Effect.Effect<Position, E | CodecError | AppendConditionFailed, DcbEventStore | Serializer | Uuid | R | RequirementOf<P>>;
205
+ export declare const runDecider: <P extends Record<string, SliceProjection>, Command, E, R, O extends DecisionOutput = Arr.NonEmptyReadonlyArray<DcbEvent>>(decider: Decider<P, Command, E, R, O>, command: Command) => Effect.Effect<DecisionResult<O>, E | CodecError | AppendConditionFailed | TaglessSliceInLedger, DcbEventStore | Serializer | Uuid | R | RequirementOf<P>>;
157
206
  //# sourceMappingURL=decider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decider.d.ts","sourceRoot":"","sources":["../../src/decider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,cAAc,EACd,QAAQ,EACR,QAAQ,EACT,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAElC;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,OAAO,CACtB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACzC,OAAO,EACP,CAAC,GAAG,KAAK,EACT,CAAC,GAAG,KAAK;IAET,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,CAAC,CAAA;IAC7C,QAAQ,CAAC,MAAM,EAAE,CACf,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EACxB,OAAO,EAAE,OAAO,KACb,MAAM,CAAC,MAAM,CAChB,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EACnC,CAAC,EACD,UAAU,GAAG,IAAI,GAAG,CAAC,CACtB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,eAAO,MAAM,OAAO,GAClB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACzC,OAAO,EACP,GAAG,SAAS,MAAM,CAAC,MAAM,CACvB,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EACnC,OAAO,EACP,OAAO,CACR,EAED,aAAa,CAAC,OAAO,EAAE,OAAO,KAAK,CAAC,EACpC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,GAAG,KAC1D,OAAO,CACR,CAAC,EACD,OAAO,EACP,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EACxB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC,CAOrD,CAAA;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GACrB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACzC,OAAO,EACP,CAAC,EACD,CAAC,EAED,SAAS,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,EAClC,SAAS,OAAO,KACf,MAAM,CAAC,MAAM,CACd,QAAQ,EACR,CAAC,GAAG,UAAU,GAAG,qBAAqB,EACtC,aAAa,GAAG,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAStD,CAAA"}
1
+ {"version":3,"file":"decider.d.ts","sourceRoot":"","sources":["../../src/decider.ts"],"names":[],"mappings":"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"}