@kairos-es/codec 0.0.0 → 1.0.0-alpha.1
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.
- package/README.md +68 -3
- package/dist/cjs/decider.js +9 -2
- package/dist/cjs/decider.js.map +1 -1
- package/dist/cjs/decisionModel.js +76 -20
- package/dist/cjs/decisionModel.js.map +1 -1
- package/dist/cjs/index.js +38 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/precondition.js +174 -0
- package/dist/cjs/precondition.js.map +1 -0
- package/dist/cjs/readLedger.js +224 -0
- package/dist/cjs/readLedger.js.map +1 -0
- package/dist/dts/decider.d.ts +11 -3
- package/dist/dts/decider.d.ts.map +1 -1
- package/dist/dts/decisionModel.d.ts +33 -13
- package/dist/dts/decisionModel.d.ts.map +1 -1
- package/dist/dts/index.d.ts +51 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/precondition.d.ts +221 -0
- package/dist/dts/precondition.d.ts.map +1 -0
- package/dist/dts/readLedger.d.ts +263 -0
- package/dist/dts/readLedger.d.ts.map +1 -0
- package/dist/esm/decider.js +9 -2
- package/dist/esm/decider.js.map +1 -1
- package/dist/esm/decisionModel.js +77 -21
- package/dist/esm/decisionModel.js.map +1 -1
- package/dist/esm/index.js +51 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/precondition.js +165 -0
- package/dist/esm/precondition.js.map +1 -0
- package/dist/esm/readLedger.js +212 -0
- package/dist/esm/readLedger.js.map +1 -0
- package/package.json +2 -2
- package/src/decider.ts +11 -3
- package/src/decisionModel.ts +87 -23
- package/src/index.ts +62 -0
- package/src/precondition.ts +333 -0
- package/src/readLedger.ts +394 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bracket that owns the append, the precondition value it takes, and the two
|
|
3
|
+
* functions that make a decision inside it writable.
|
|
4
|
+
*
|
|
5
|
+
* ## What the bracket is for, and why no condition reaches the caller
|
|
6
|
+
*
|
|
7
|
+
* `transact(precondition)(body)` evaluates a guard, hands its output to the body
|
|
8
|
+
* as a service, and appends whatever the body produced under the union of every
|
|
9
|
+
* read EITHER of them performed. A caller writes no condition, no union, no floor
|
|
10
|
+
* and no `min`.
|
|
11
|
+
*
|
|
12
|
+
* That is not an ergonomic preference. Owning the append does not make a wrong
|
|
13
|
+
* condition a type error — it makes one UNWRITABLE, which is the stronger
|
|
14
|
+
* guarantee: a returned condition is a condition a caller can decline to use, and
|
|
15
|
+
* a decision that looks guarded and is not is the exact failure this seam exists
|
|
16
|
+
* to close. It is what Axon Framework 5 does, where the condition is married to
|
|
17
|
+
* the events at prepare-commit and the caller never holds one.
|
|
18
|
+
*
|
|
19
|
+
* One thing is deliberately NOT borrowed from that prior art. There, an append
|
|
20
|
+
* with no prior read leaves the condition unset and the append unguarded, so the
|
|
21
|
+
* method name reads as unconditional while the behaviour is not. `NoReadsRecorded`
|
|
22
|
+
* closes that door here and must not be lost.
|
|
23
|
+
*
|
|
24
|
+
* ⚠ The name says *declare, then do*. It is NOT a database transaction: an append
|
|
25
|
+
* under a condition is not two-phase, and nothing rolls back.
|
|
26
|
+
*
|
|
27
|
+
* ## Order is the design, and it is the one line here that must not move
|
|
28
|
+
*
|
|
29
|
+
* The precondition is evaluated INSIDE the bracket and never before it. Evaluated
|
|
30
|
+
* before, its reads are not part of what it protects — the ledger does not exist
|
|
31
|
+
* yet, so its hops are lost and the write is guarded only by the business read.
|
|
32
|
+
* The check would look exactly as it does now and would guard nothing.
|
|
33
|
+
*
|
|
34
|
+
* ## Why the precondition's output crosses as a SERVICE
|
|
35
|
+
*
|
|
36
|
+
* A body that needs authorisation names the tag in its requirement channel, so
|
|
37
|
+
* `transact` must provide it, so the body CANNOT BE RUN without a precondition.
|
|
38
|
+
* That is a PRESENCE check, and presence is the one direction the type system can
|
|
39
|
+
* decide here — `Exclude<R, Id>` in the return type is where it is spent. The
|
|
40
|
+
* alternative, a curried parameter, buys nothing equivalent: a body is free to
|
|
41
|
+
* ignore a parameter.
|
|
42
|
+
*
|
|
43
|
+
* ## Composition is a combinator, not a list
|
|
44
|
+
*
|
|
45
|
+
* `transact` takes exactly ONE precondition. A variadic argument asserts
|
|
46
|
+
* INDEPENDENCE, and the interesting case is DEPENDENT — *may act for this
|
|
47
|
+
* organisation*, then *may sign in that organisation* — which a flat list cannot
|
|
48
|
+
* express and which is the very shape this change exists for. A dependent pair
|
|
49
|
+
* needs nothing anyway: a precondition is an ordinary `Effect` evaluated inside
|
|
50
|
+
* the open ledger, so two compose with `Effect.gen` and both sets of reads land in
|
|
51
|
+
* the same condition. An INDEPENDENT set wants a combinator over a RECORD,
|
|
52
|
+
* evaluated SEQUENTIALLY — fail-fast is the point, and concurrent reads are
|
|
53
|
+
* precisely the case where the earliest-head reduction stops being redundant. That
|
|
54
|
+
* combinator is purely additive and is not shipped here.
|
|
55
|
+
*
|
|
56
|
+
* ## Conflict retry goes OUTSIDE the bracket, never inside
|
|
57
|
+
*
|
|
58
|
+
* `retryOnConflict(transact(p)(body))`, never a retry within the body. The bracket
|
|
59
|
+
* takes an opaque `Effect`, so no type can express that rule and the lint gate is
|
|
60
|
+
* what enforces it: in the source repository, `biome-plugins/noRetryInBracket.grit`
|
|
61
|
+
* owns why and `test/retryInBracket.test.ts` executes it.
|
|
62
|
+
*
|
|
63
|
+
* ## ⚠⚠ THE ONE MISTAKE NOTHING CATCHES: a view read inside a bracket
|
|
64
|
+
*
|
|
65
|
+
* **A projection read, a raw SQL read, or any read that does not fold through
|
|
66
|
+
* `modelAtHead`, performed inside a bracket, contributes NO HOP.** Its query is
|
|
67
|
+
* absent from the derived union and its head is absent from the floor, so the
|
|
68
|
+
* decision rests on data the condition does not guard — and the bracket looks
|
|
69
|
+
* guarded, which is the exact failure this seam exists to close, wearing the fix's
|
|
70
|
+
* own name.
|
|
71
|
+
*
|
|
72
|
+
* **Nothing will tell you — not here and not in your own tree.** Not the type
|
|
73
|
+
* system: a requirement channel is ADDITIVE, so it can demand a capability's
|
|
74
|
+
* presence and never its absence. Not `NoReadsRecorded`: that fires on an EMPTY
|
|
75
|
+
* bracket and never on an under-populated one, so a bracket holding one genuine fold
|
|
76
|
+
* beside a view read passes it. And not a lint rule — one was written for exactly
|
|
77
|
+
* this, kept for a while and deleted, because a syntactic gate has no call graph and
|
|
78
|
+
* no types, so it could only name a receiver by convention, and because a lint
|
|
79
|
+
* plugin runs only where its own config is and so never travels with the library.
|
|
80
|
+
* `biome-plugins/README.md` in the source repository records the test that rule
|
|
81
|
+
* failed.
|
|
82
|
+
*
|
|
83
|
+
* So this is a rule a reader keeps, and knowing it is the guarantee: **inside a
|
|
84
|
+
* bracket, read through `modelAtHead`. If a read must
|
|
85
|
+
* consult a materialised view, do it OUTSIDE the bracket** — where it is ordinary
|
|
86
|
+
* code, because its answer is then not part of what the append is guarded on.
|
|
87
|
+
* ADR-0014's *decision read* rule is the test for which one a given read is.
|
|
88
|
+
*/
|
|
89
|
+
import type { AppendConditionFailed, DcbEvent, Position } from '@kairos-es/core';
|
|
90
|
+
import { AppendIsOwned, DcbEventStore } from '@kairos-es/core';
|
|
91
|
+
import type { Array as Arr } from 'effect';
|
|
92
|
+
import { Context, Effect } from 'effect';
|
|
93
|
+
import type { Decider } from './decider.js';
|
|
94
|
+
import { type RequirementOf } from './decisionModel.js';
|
|
95
|
+
import type { CodecError } from './errors.js';
|
|
96
|
+
import type { SliceProjection } from './projection.js';
|
|
97
|
+
import { NoReadsRecorded, ReadLedger, type TaglessSliceInLedger } from './readLedger.js';
|
|
98
|
+
import type { Serializer } from './Serializer.js';
|
|
99
|
+
import type { Uuid } from './Uuid.js';
|
|
100
|
+
/**
|
|
101
|
+
* A named, reusable guard plus the tag its output is handed across.
|
|
102
|
+
*
|
|
103
|
+
* A VALUE and not a function of the command, deliberately: the point of the seam
|
|
104
|
+
* is that a guard is written once, centrally, and referenced by however many
|
|
105
|
+
* commands need it. A command supplies its own arguments when it BUILDS the value
|
|
106
|
+
* (`mayActFor(userId, orgId, verb)`), and what `transact` receives is the built
|
|
107
|
+
* thing.
|
|
108
|
+
*
|
|
109
|
+
* `Service` is fully generic and the library never reads it. Every consumer-side
|
|
110
|
+
* authorisation shape — a plain resolved identifier, a branded proof, a gate with
|
|
111
|
+
* a private minting closure — is application code this genericity accommodates
|
|
112
|
+
* without the library knowing anything about it. That is a requirement rather than
|
|
113
|
+
* an incidental property: hard-coding it would foreclose a consumer's later
|
|
114
|
+
* choice, and it costs nothing to keep.
|
|
115
|
+
*
|
|
116
|
+
* ⚠ `evaluate` is an `Effect`, so it is a FIELD OF AN OBJECT LITERAL and its
|
|
117
|
+
* expression is evaluated when the `Precondition` VALUE is built — before the
|
|
118
|
+
* bracket opens and before any cheap check inside it runs. Anything a builder does
|
|
119
|
+
* eagerly there (parsing an argument, resolving an identifier) happens outside the
|
|
120
|
+
* ordering guarantee above, and a throw there is an uncatchable defect on the very
|
|
121
|
+
* path meant to deny the input cheaply. Wrap such work in `Effect.suspend`.
|
|
122
|
+
*/
|
|
123
|
+
export interface Precondition<Id, Service, E, R> {
|
|
124
|
+
readonly tag: Context.Tag<Id, Service>;
|
|
125
|
+
readonly evaluate: Effect.Effect<Service, E, R>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The DCB command cycle MINUS the append: read the decision model, decode, fold,
|
|
129
|
+
* decide, and RETURN the events for the bracket to append.
|
|
130
|
+
*
|
|
131
|
+
* Bracket-only, and the refusal is STATIC — see the section below for the mechanism
|
|
132
|
+
* and, more importantly, for where the error actually lands, which is not where a
|
|
133
|
+
* reader first expects. Forced through anyway, being outside a bracket is a DEFECT
|
|
134
|
+
* rather than an error-channel value: it is not a data-dependent outcome a caller
|
|
135
|
+
* could handle, and the events produced would be dropped with nothing to append them.
|
|
136
|
+
*
|
|
137
|
+
* It is also what makes the nesting refusal liveable. A command runner inside a
|
|
138
|
+
* bracket is refused at the store, and this is the function a builder reaches for
|
|
139
|
+
* instead — one identifier away. A refusal is only affordable when the fix is that
|
|
140
|
+
* close.
|
|
141
|
+
*
|
|
142
|
+
* `modelAtHead` and NOT `buildDecisionModel`: this function must not derive a
|
|
143
|
+
* condition at all. The bracket owns that, and owning it in ONE place is what
|
|
144
|
+
* stops a caller appending under the last hop's condition alone.
|
|
145
|
+
*
|
|
146
|
+
* ## Bracket-only is a REQUIREMENT, so `tsc` refuses it rather than a run
|
|
147
|
+
*
|
|
148
|
+
* `ReadLedger` is named in the requirement channel and `transact` is the only thing
|
|
149
|
+
* that provides it, so a program that calls this outside a bracket cannot be RUN,
|
|
150
|
+
* and `tsc` is what says so — nothing has to execute for a caller to be told.
|
|
151
|
+
*
|
|
152
|
+
* ⚠ Measured, because the obvious stronger reading is wrong: the call site itself
|
|
153
|
+
* COMPILES. A bare `decideOnly(d, cmd)` outside a bracket is fine, and so is an
|
|
154
|
+
* exported helper over it; the requirement just propagates, as any unmet requirement
|
|
155
|
+
* does. What fails is the DISCHARGE — `Effect.provide`, `runPromise`, an annotation
|
|
156
|
+
* asking for `R = never` — possibly in another file. That is still a static refusal
|
|
157
|
+
* and it is the whole gain over the runtime defect this replaced. It is not a
|
|
158
|
+
* squiggle under the mistake.
|
|
159
|
+
*
|
|
160
|
+
* That is a PRESENCE check, and presence is the direction the type system can
|
|
161
|
+
* decide here — the same direction `transact`'s `Exclude<R, Id>` already spends on
|
|
162
|
+
* the precondition's output. ADR-0014's 2026-08-29 amendment carries the rule that
|
|
163
|
+
* follows: look for a presence check before reaching for a defect. This function
|
|
164
|
+
* shipped a defect first, and the check was available all along.
|
|
165
|
+
*
|
|
166
|
+
* ⚠ Two lines carry it and their JOBS differ. The RETURN ANNOTATION is the surface a
|
|
167
|
+
* consumer is judged against, and what the discharge above meets. The
|
|
168
|
+
* `yield* ReadLedger` keeps the requirement in the INFERRED type, so it survives the
|
|
169
|
+
* annotation being edited, and it dies at run time for the audience no type reaches
|
|
170
|
+
* — a JavaScript consumer runs no compile step — rather than handing back events
|
|
171
|
+
* with no bracket to append them. Nothing here READS the ledger; `modelAtHead` does
|
|
172
|
+
* the recording, ambiently, through `Effect.serviceOption`.
|
|
173
|
+
*
|
|
174
|
+
* ⚠ Do not try to write down which check catches which deletion. It was tried twice
|
|
175
|
+
* and was wrong twice, and `test/appendOwnership.test.ts` in the source repository
|
|
176
|
+
* says why the second failure means there should be no third attempt. Plant the
|
|
177
|
+
* mutation instead.
|
|
178
|
+
*/
|
|
179
|
+
export declare const decideOnly: <P extends Record<string, SliceProjection>, Command, E, R>(decider: Decider<P, Command, E, R>, command: Command) => Effect.Effect<Arr.NonEmptyReadonlyArray<DcbEvent>, E | CodecError | TaglessSliceInLedger, ReadLedger | DcbEventStore | Serializer | Uuid | R | RequirementOf<P>>;
|
|
180
|
+
/**
|
|
181
|
+
* The sanctioned exception to the nesting refusal: append these events under NO
|
|
182
|
+
* condition, and say in the identifier and in a required `reason` why that is
|
|
183
|
+
* deliberate.
|
|
184
|
+
*
|
|
185
|
+
* It covers three cases and no others: deliberate narrowing, a genuinely
|
|
186
|
+
* unconditional creation, and a second append inside one bracket. The `reason` is
|
|
187
|
+
* ceremony on purpose, and ADR-0014 decision 6 owns why a hatch has to cost more
|
|
188
|
+
* than the safe path — that argument is a DECISION and belongs to the record that
|
|
189
|
+
* took it, so it is not restated here. How biome turns a required argument into a
|
|
190
|
+
* review signal is the source repository's `biome-plugins/unsafeAppendReported.grit`.
|
|
191
|
+
*
|
|
192
|
+
* ⚠ It BYPASSES the derived condition rather than composing with it. A caller who
|
|
193
|
+
* genuinely needs *the union, minus one event type* must restate the whole
|
|
194
|
+
* condition here. If that turns up in practice, a composing override is the
|
|
195
|
+
* additive answer and this hatch is not it.
|
|
196
|
+
*
|
|
197
|
+
* `Effect.mapInputContext` over
|
|
198
|
+
* `Context.omit(AppendIsOwned)` is what lets it through the store's ownership
|
|
199
|
+
* refusal — the one route, and `AppendIsOwned.ts` owns why removing the marker is
|
|
200
|
+
* the shape rather than a second tag or a sanctioning flag.
|
|
201
|
+
*/
|
|
202
|
+
export declare const unsafeAppend: (events: Arr.NonEmptyReadonlyArray<DcbEvent>, reason: string) => Effect.Effect<Position, AppendConditionFailed, DcbEventStore>;
|
|
203
|
+
/**
|
|
204
|
+
* Open a bracket over ONE decision: evaluate the precondition inside it, hand its
|
|
205
|
+
* output to the body under the precondition's tag, and append what the body
|
|
206
|
+
* produced under the union of every read either of them performed.
|
|
207
|
+
*
|
|
208
|
+
* Curried so the precondition reads as the thing the command DECLARES and the body
|
|
209
|
+
* reads as the thing it DOES.
|
|
210
|
+
*
|
|
211
|
+
* The ownership marker is provided ALONGSIDE the ledger and to the body only, so
|
|
212
|
+
* any append inside the body — a nested bracket, a command runner, a raw
|
|
213
|
+
* `store.append` — is refused at the store as a defect, while the bracket's own
|
|
214
|
+
* closing append below runs outside that scope and is unaffected. That is why the
|
|
215
|
+
* refusal is on APPENDING rather than on opening a bracket: a command runner
|
|
216
|
+
* appends without opening one, and its events usually match the union the bracket
|
|
217
|
+
* is about to guard on, so the bracket's own close would be refused by a condition
|
|
218
|
+
* its own body invalidated.
|
|
219
|
+
*/
|
|
220
|
+
export declare const transact: <Id, Service, PE, PR>(precondition: Precondition<Id, Service, PE, PR>) => <E, R>(body: Effect.Effect<Arr.NonEmptyReadonlyArray<DcbEvent>, E, R>) => Effect.Effect<Position, E | PE | NoReadsRecorded | AppendConditionFailed, Exclude<Exclude<R, Id>, ReadLedger | AppendIsOwned> | Exclude<PR, ReadLedger | AppendIsOwned> | DcbEventStore>;
|
|
221
|
+
//# sourceMappingURL=precondition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"precondition.d.ts","sourceRoot":"","sources":["../../src/precondition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AACH,OAAO,KAAK,EAAE,qBAAqB,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAS,MAAM,QAAQ,CAAA;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAGL,eAAe,EACf,UAAU,EACV,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAElC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,YAAY,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;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,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EACnC,CAAC,GAAG,UAAU,GAAG,oBAAoB,EACrC,UAAU,GAAG,aAAa,GAAG,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAOnE,CAAA;AAEJ;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,YAAY,GACvB,QAAQ,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAc3C,QAAQ,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,qBAAqB,EAAE,aAAa,CAe5D,CAAA;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,GAClB,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,cAAc,YAAY,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC,MACpE,CAAC,EAAE,CAAC,EACH,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7D,MAAM,CAAC,MAAM,CACd,QAAQ,EACR,CAAC,GAAG,EAAE,GAAG,eAAe,GAAG,qBAAqB,EAC9C,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC,GACnD,OAAO,CAAC,EAAE,EAAE,UAAU,GAAG,aAAa,CAAC,GACvC,aAAa,CA6Cb,CAAA"}
|
|
@@ -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"}
|
package/dist/esm/decider.js
CHANGED
|
@@ -128,8 +128,15 @@ export const decider = (projections, decide) => ({
|
|
|
128
128
|
* (`buildDecisionModel`) and produce (`toEvent`) sides, `Uuid` through produce,
|
|
129
129
|
* the slices' `RequirementOf<P>` through decode, and `R` through produce. (For a
|
|
130
130
|
* def whose `Schema` carries a requirement, `RequirementOf<P>` and `R` are the
|
|
131
|
-
* same tag.) Any `decide` failure, a decode `CodecError`,
|
|
132
|
-
* `AppendConditionFailed
|
|
131
|
+
* same tag.) Any `decide` failure, a decode `CodecError`, an
|
|
132
|
+
* `AppendConditionFailed`, or the read's own `TaglessSliceInLedger` propagates on
|
|
133
|
+
* the error channel unchanged.
|
|
134
|
+
*
|
|
135
|
+
* ⚠ INSIDE a bracket this function is REFUSED, as a defect, at the store's shared
|
|
136
|
+
* append decorator, and `decideOnly` is what a body reaches for instead.
|
|
137
|
+
* `index.ts` states that pair, why each refuses exactly where the other is the
|
|
138
|
+
* answer, and why neither can be expressed over the other. Read it before
|
|
139
|
+
* changing either signature; this file does not restate the argument.
|
|
133
140
|
*/
|
|
134
141
|
export const runDecider = (decider, command) => Effect.gen(function* () {
|
|
135
142
|
const store = yield* DcbEventStore;
|
package/dist/esm/decider.js.map
CHANGED
|
@@ -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;
|
|
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;AAuCxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,OAAO,MAAMC,OAAO,GAAGA,CASrBC,WAAoC,EACpCC,MAA2D,MAO1D;EAAED,WAAW;EAAEC;AAAM,CAAE,CAKvB;AAEH;;;;;;;;;;;;;;;;;;;;AAoBA,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":[]}
|