@kairos-es/codec 1.0.0-alpha.4 → 1.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cjs/MetadataSource.js +1 -1
- package/dist/cjs/MetadataSource.js.map +1 -1
- package/dist/cjs/Serializer.js +3 -3
- package/dist/cjs/Serializer.js.map +1 -1
- package/dist/cjs/Uuid.js +6 -3
- package/dist/cjs/Uuid.js.map +1 -1
- package/dist/cjs/decider.js +1 -1
- package/dist/cjs/decisionModel.js +1 -1
- package/dist/cjs/decisionModel.js.map +1 -1
- package/dist/cjs/decode.js +5 -5
- package/dist/cjs/decode.js.map +1 -1
- package/dist/cjs/deserialiseThenDecode.js +1 -1
- package/dist/cjs/deserialiseThenDecode.js.map +1 -1
- package/dist/cjs/errors.js +6 -4
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/event.js.map +1 -1
- package/dist/cjs/precondition.js +2 -2
- package/dist/cjs/precondition.js.map +1 -1
- package/dist/cjs/projection.js +9 -10
- package/dist/cjs/projection.js.map +1 -1
- package/dist/cjs/readLedger.js +3 -3
- package/dist/cjs/readLedger.js.map +1 -1
- package/dist/cjs/registry.js.map +1 -1
- package/dist/cjs/toEvent.js +4 -4
- package/dist/cjs/toEvent.js.map +1 -1
- package/dist/dts-cjs/MetadataSource.d.ts +8 -0
- package/dist/{dts → dts-cjs}/MetadataSource.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/Serializer.d.ts +3 -3
- package/dist/{dts → dts-cjs}/Serializer.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/Uuid.d.ts +6 -3
- package/dist/{dts → dts-cjs}/Uuid.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/decider.d.ts +7 -7
- package/dist/{dts → dts-cjs}/decider.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/decision.d.ts +2 -2
- package/dist/{dts → dts-cjs}/decode.d.ts +3 -3
- package/dist/{dts → dts-cjs}/decode.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/deserialiseThenDecode.d.ts +3 -3
- package/dist/{dts → dts-cjs}/deserialiseThenDecode.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/errors.d.ts +4 -3
- package/dist/dts-cjs/errors.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/event.d.ts +18 -8
- package/dist/dts-cjs/event.d.ts.map +1 -0
- package/dist/dts-cjs/package.json +3 -0
- package/dist/{dts → dts-cjs}/precondition.d.ts +2 -2
- package/dist/{dts → dts-cjs}/projection.d.ts +2 -2
- package/dist/dts-cjs/projection.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/readLedger.d.ts +4 -4
- package/dist/{dts → dts-cjs}/readLedger.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/toEvent.d.ts +2 -2
- package/dist/dts-esm/MetadataSource.d.ts +8 -0
- package/dist/dts-esm/MetadataSource.d.ts.map +1 -0
- package/dist/dts-esm/Serializer.d.ts +27 -0
- package/dist/dts-esm/Serializer.d.ts.map +1 -0
- package/dist/dts-esm/Uuid.d.ts +22 -0
- package/dist/dts-esm/Uuid.d.ts.map +1 -0
- package/dist/dts-esm/decider.d.ts +206 -0
- package/dist/dts-esm/decider.d.ts.map +1 -0
- package/dist/dts-esm/decision.d.ts +161 -0
- package/dist/dts-esm/decision.d.ts.map +1 -0
- package/dist/dts-esm/decisionModel.d.ts +149 -0
- package/dist/dts-esm/decisionModel.d.ts.map +1 -0
- package/dist/dts-esm/decode.d.ts +92 -0
- package/dist/dts-esm/decode.d.ts.map +1 -0
- package/dist/dts-esm/deserialiseThenDecode.d.ts +62 -0
- package/dist/dts-esm/deserialiseThenDecode.d.ts.map +1 -0
- package/dist/dts-esm/errors.d.ts +80 -0
- package/dist/dts-esm/errors.d.ts.map +1 -0
- package/dist/dts-esm/event.d.ts +81 -0
- package/dist/dts-esm/event.d.ts.map +1 -0
- package/dist/dts-esm/index.d.ts +134 -0
- package/dist/dts-esm/index.d.ts.map +1 -0
- package/dist/dts-esm/package.json +3 -0
- package/dist/dts-esm/precondition.d.ts +244 -0
- package/dist/dts-esm/precondition.d.ts.map +1 -0
- package/dist/dts-esm/projection.d.ts +56 -0
- package/dist/dts-esm/projection.d.ts.map +1 -0
- package/dist/dts-esm/readLedger.d.ts +263 -0
- package/dist/dts-esm/readLedger.d.ts.map +1 -0
- package/dist/dts-esm/registry.d.ts +46 -0
- package/dist/dts-esm/registry.d.ts.map +1 -0
- package/dist/dts-esm/toEvent.d.ts +45 -0
- package/dist/dts-esm/toEvent.d.ts.map +1 -0
- package/dist/esm/MetadataSource.js +1 -1
- package/dist/esm/MetadataSource.js.map +1 -1
- package/dist/esm/Serializer.js +3 -3
- package/dist/esm/Serializer.js.map +1 -1
- package/dist/esm/Uuid.js +6 -3
- package/dist/esm/Uuid.js.map +1 -1
- package/dist/esm/decider.js +1 -1
- package/dist/esm/decisionModel.js +1 -1
- package/dist/esm/decisionModel.js.map +1 -1
- package/dist/esm/decode.js +5 -5
- package/dist/esm/decode.js.map +1 -1
- package/dist/esm/deserialiseThenDecode.js +1 -1
- package/dist/esm/deserialiseThenDecode.js.map +1 -1
- package/dist/esm/errors.js +6 -4
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/event.js.map +1 -1
- package/dist/esm/precondition.js +2 -2
- package/dist/esm/precondition.js.map +1 -1
- package/dist/esm/projection.js +9 -10
- package/dist/esm/projection.js.map +1 -1
- package/dist/esm/readLedger.js +3 -3
- package/dist/esm/readLedger.js.map +1 -1
- package/dist/esm/registry.js.map +1 -1
- package/dist/esm/toEvent.js +4 -4
- package/dist/esm/toEvent.js.map +1 -1
- package/package.json +12 -7
- package/src/MetadataSource.ts +2 -2
- package/src/Serializer.ts +4 -4
- package/src/Uuid.ts +7 -4
- package/src/decider.ts +9 -9
- package/src/decision.ts +1 -1
- package/src/decisionModel.ts +1 -1
- package/src/decode.ts +12 -18
- package/src/deserialiseThenDecode.ts +5 -5
- package/src/errors.ts +6 -4
- package/src/event.ts +19 -9
- package/src/precondition.ts +3 -3
- package/src/projection.ts +15 -20
- package/src/readLedger.ts +7 -7
- package/src/registry.ts +1 -1
- package/src/toEvent.ts +4 -4
- package/dist/dts/MetadataSource.d.ts +0 -8
- package/dist/dts/errors.d.ts.map +0 -1
- package/dist/dts/event.d.ts.map +0 -1
- package/dist/dts/projection.d.ts.map +0 -1
- /package/dist/{dts → dts-cjs}/decision.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/decisionModel.d.ts +0 -0
- /package/dist/{dts → dts-cjs}/decisionModel.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/index.d.ts +0 -0
- /package/dist/{dts → dts-cjs}/index.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/precondition.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/registry.d.ts +0 -0
- /package/dist/{dts → dts-cjs}/registry.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/toEvent.d.ts.map +0 -0
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
* than memory.
|
|
21
21
|
*
|
|
22
22
|
* Be precise about what adding `concurrency` here would and would not cost,
|
|
23
|
-
* because the obvious reason is the wrong one. Effect
|
|
23
|
+
* because the obvious reason is the wrong one. Effect's `mapEffect` is
|
|
24
24
|
* order-PRESERVING even when concurrent — reordering needs the separate
|
|
25
|
-
* `unordered: true` option (verified against
|
|
26
|
-
*
|
|
25
|
+
* `unordered: true` option (verified against `effect@4`'s `Channel.mapEffect`,
|
|
26
|
+
* which takes the ordered path unless that option is set) — so a bare
|
|
27
27
|
* `concurrency` would NOT hand the fold its events out of position order. What
|
|
28
28
|
* it would actually cost is the back-pressure above, and it would run
|
|
29
29
|
* application-authored `Schema` transforms (an ADR-0004 decryption hook, say)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../src/decode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAuB,MAAM,EAAE,MAAM,QAAQ,CAAA;AAIpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../src/decode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAuB,MAAM,EAAE,MAAM,QAAQ,CAAA;AAIpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAoJzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACpE,QAAQ,CAAC,KACR,CAAC,CAAC,CAAC,EAAE,CAAC,EACP,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC,KACxC,MAAM,CAAC,MAAM,CAChB,YAAY,EACZ,CAAC,GAAG,UAAU,EACd,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,UAAU,CAClC,CAA4C,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The two-step every decode in this package performs, in one implementation:
|
|
3
3
|
* deserialise the wire bytes through the `Serializer` its caller already holds,
|
|
4
|
-
* then `Schema.
|
|
4
|
+
* then `Schema.decodeUnknownEffect` the result, each half's failure wrapped as the
|
|
5
5
|
* `CodecError` whose `reason` says WHICH half failed.
|
|
6
6
|
*
|
|
7
7
|
* **What it is the single home of.** The two `reason` strings `'deserialise'`
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* as a function and not as a second test.
|
|
18
18
|
*
|
|
19
19
|
* **The order is contract, not accident.** Deserialise has to run first because
|
|
20
|
-
* `Schema.
|
|
20
|
+
* `Schema.decodeUnknownEffect` has nothing to read until it has, and the observable
|
|
21
21
|
* consequence is that the two reasons PARTITION the causes an operator acts on:
|
|
22
22
|
* rotten bytes are always `'deserialise'` and never `'decode'`, so a storage or
|
|
23
23
|
* producer problem never arrives dressed as schema drift. Swapping the labels
|
|
@@ -58,5 +58,5 @@ import type { Context } from 'effect';
|
|
|
58
58
|
import { Effect, Schema } from 'effect';
|
|
59
59
|
import { CodecError } from './errors.js';
|
|
60
60
|
import type { Serializer } from './Serializer.js';
|
|
61
|
-
export declare const deserialiseThenDecode: <R>(serializer: Context.
|
|
61
|
+
export declare const deserialiseThenDecode: <R>(serializer: Context.Service.Shape<typeof Serializer>, schema: Schema.Decoder<unknown, R>, bytes: Uint8Array) => Effect.Effect<unknown, CodecError, R>;
|
|
62
62
|
//# sourceMappingURL=deserialiseThenDecode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deserialiseThenDecode.d.ts","sourceRoot":"","sources":["../../src/deserialiseThenDecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,eAAO,MAAM,qBAAqB,GAAI,CAAC,EACrC,YAAY,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"deserialiseThenDecode.d.ts","sourceRoot":"","sources":["../../src/deserialiseThenDecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,eAAO,MAAM,qBAAqB,GAAI,CAAC,EACrC,YAAY,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,EACpD,QAAQ,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAClC,OAAO,UAAU,KAChB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC,CAUpC,CAAA"}
|
|
@@ -30,9 +30,10 @@ export declare class CodecError extends CodecError_base<{
|
|
|
30
30
|
*
|
|
31
31
|
* A GETTER rather than a second field, so `reason` stays the single declaration.
|
|
32
32
|
* It is visible because `Data.TaggedError`'s constructor is
|
|
33
|
-
* `super(args?.message, …)` followed by
|
|
34
|
-
* against `effect@
|
|
35
|
-
* nothing shadows this prototype accessor
|
|
33
|
+
* `super(args?.message, …)` followed by copying the args' own enumerable keys
|
|
34
|
+
* onto the instance (verified against `effect@4`'s `internal/core.ts`): the
|
|
35
|
+
* args above carry no `message`, so nothing shadows this prototype accessor
|
|
36
|
+
* with an own property.
|
|
36
37
|
*/
|
|
37
38
|
get message(): string;
|
|
38
39
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAgBA,2EAA2E;AAC3E,qBAAa,UAAW,SAAQ,gBAA+B;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB,CAAC;IACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;;;;AAED,kFAAkF;AAClF,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAC;IACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event definitions: a tagged `Schema` built through `event(type, fields,
|
|
3
|
-
* tagsFor)`. Each def is a `Schema<Payload, Wire, R>` plus its branded versioned
|
|
3
|
+
* tagsFor)`. Each def is a `Schema.Codec<Payload, Wire, R, R>` plus its branded versioned
|
|
4
4
|
* `type` (ADR-0005) captured *beside* the payload, and a required `tagsFor`
|
|
5
5
|
* mapper deriving the event's tags from its decoded payload (ADR-0006).
|
|
6
6
|
*
|
|
@@ -40,19 +40,29 @@ export type TagsFor<A> = (payload: A) => Arr.NonEmptyReadonlyArray<Tag>;
|
|
|
40
40
|
/**
|
|
41
41
|
* An event definition: its branded `type`, the `Schema` mapping its typed
|
|
42
42
|
* payload `A` to its wire encoding `I` (requiring `R`), and the `tagsFor` mapper
|
|
43
|
-
* that derives the event's tags from a decoded payload.
|
|
44
|
-
*
|
|
43
|
+
* that derives the event's tags from a decoded payload.
|
|
44
|
+
*
|
|
45
|
+
* `R` is ONE requirement for both directions, which is why the schema is a
|
|
46
|
+
* `Schema.Codec<A, I, R, R>` rather than leaving decoding and encoding services
|
|
47
|
+
* apart. A def is decoded on the read path and encoded by `toEvent`, so a
|
|
48
|
+
* consumer providing a def's services provides them for both; an application
|
|
49
|
+
* encryption transform needs its key either way. A struct whose two directions
|
|
50
|
+
* differ still fits, the codec being covariant in both, and `R` is then their
|
|
51
|
+
* union.
|
|
52
|
+
*
|
|
53
|
+
* `tagsFor` CONSUMES an `A`, so `EventDef` is invariant in `A` whatever the
|
|
54
|
+
* schema's own variance, and is therefore not declared `out A`.
|
|
45
55
|
*/
|
|
46
56
|
export interface EventDef<A = unknown, I = unknown, R = never> {
|
|
47
57
|
readonly type: EventType;
|
|
48
|
-
readonly schema: Schema.
|
|
58
|
+
readonly schema: Schema.Codec<A, I, R, R>;
|
|
49
59
|
readonly tagsFor: TagsFor<A>;
|
|
50
60
|
}
|
|
51
61
|
/**
|
|
52
|
-
* The heterogeneous bound
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
62
|
+
* The heterogeneous bound for arrays/records of mixed defs, where the precise
|
|
63
|
+
* `A`/`I` differ per element. `tagsFor` erases to `(a: any) => …`, which is
|
|
64
|
+
* harmless: only the write path (`toEvent`) calls it, and the registry/decode
|
|
65
|
+
* path never does (stored events already carry tags).
|
|
56
66
|
*/
|
|
57
67
|
export type EventDefAny = EventDef<any, any, unknown>;
|
|
58
68
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/event.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;;;GAKG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,GAAG,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAA;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,KAAK;IAC3D,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAC7B;AAED;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,GAChB,KAAK,CAAC,IAAI,SAAS,MAAM,EACzB,MAAM,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EAEnC,MAAM,IAAI,EACV,QAAQ,MAAM,EACd,SAAS,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAC3C;IACD,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;CAStD,CAAA"}
|
|
@@ -137,7 +137,7 @@ import type { Uuid } from './Uuid.js';
|
|
|
137
137
|
* path meant to deny the input cheaply. Wrap such work in `Effect.suspend`.
|
|
138
138
|
*/
|
|
139
139
|
export interface Precondition<Id, Service, E, R> {
|
|
140
|
-
readonly tag: Context.
|
|
140
|
+
readonly tag: Context.Key<Id, Service>;
|
|
141
141
|
readonly evaluate: Effect.Effect<Service, E, R>;
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
@@ -211,7 +211,7 @@ export declare const decideOnly: <P extends Record<string, SliceProjection>, Com
|
|
|
211
211
|
* condition here. If that turns up in practice, a composing override is the
|
|
212
212
|
* additive answer and this hatch is not it.
|
|
213
213
|
*
|
|
214
|
-
* `Effect.
|
|
214
|
+
* `Effect.updateContext` over
|
|
215
215
|
* `Context.omit(AppendIsOwned)` is what lets it through the store's ownership
|
|
216
216
|
* refusal — the one route, and `AppendIsOwned.ts` owns why removing the marker is
|
|
217
217
|
* the shape rather than a second tag or a sanctioning flag.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* A `SliceProjection<S, R>` is a core `AnyProjection<S>` (directly composable —
|
|
6
6
|
* its `evolve` folds a `DecodedEvent`) enriched with the `type -> Schema` map
|
|
7
7
|
* `decodeSlices` merges and decodes through. That map's value type carries `R`
|
|
8
|
-
* covariantly (`Schema<unknown,
|
|
8
|
+
* covariantly (`Schema.Decoder<unknown, R>`), so the union of the defs'
|
|
9
9
|
* requirements flows to the decision model with no cast (and no phantom marker).
|
|
10
10
|
* Because `evolve` is already erased, a built slice drops straight into
|
|
11
11
|
* `composeProjections` — no `AnyProjection.of` ceremony.
|
|
@@ -27,7 +27,7 @@ import type { EventDef } from './event.js';
|
|
|
27
27
|
* (and the `out R` annotation keeps union-widening sound) with no phantom marker.
|
|
28
28
|
*/
|
|
29
29
|
export interface SliceProjection<out S = unknown, out R = unknown> extends AnyProjection<S> {
|
|
30
|
-
readonly schemas: ReadonlyMap<string, Schema.
|
|
30
|
+
readonly schemas: ReadonlyMap<string, Schema.Decoder<unknown, R>>;
|
|
31
31
|
}
|
|
32
32
|
/** The fluent builder: accumulate `.on(def, fold)` steps, then `.build()`. */
|
|
33
33
|
export interface ProjectionBuilder<S, R = never> {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../src/projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,KAAK,aAAa,EAIlB,KAAK,GAAG,EACT,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACpC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,WAAW,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,CAAC,CAAC,GAAG,OAAO,CAC/D,SAAQ,aAAa,CAAC,CAAC,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;CAClE;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK;IAC7C;;;;;;;OAOG;IACH,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EACT,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EACvB,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAChC,iBAAiB,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;IAC/B;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAA;KAAE,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CAC/E;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,cAAc,CAAC,KAAG,iBAAiB,CAAC,CAAC,EAAE,KAAK,CA4FzE,CAAA"}
|
|
@@ -76,7 +76,7 @@ export interface Hop {
|
|
|
76
76
|
readonly query: Query;
|
|
77
77
|
readonly head?: Position;
|
|
78
78
|
}
|
|
79
|
-
declare const ReadLedger_base: Context.
|
|
79
|
+
declare const ReadLedger_base: Context.ServiceClass<ReadLedger, "@kairos-es/codec/ReadLedger", {
|
|
80
80
|
/**
|
|
81
81
|
* File a query, and hand back the effect that will fill in its head.
|
|
82
82
|
*
|
|
@@ -101,7 +101,7 @@ declare const ReadLedger_base: Context.TagClass<ReadLedger, "@kairos-es/codec/Re
|
|
|
101
101
|
* requirement channel nothing is exactly what makes this change additive.
|
|
102
102
|
* `modelAtHead` is that path and is why the rule exists.
|
|
103
103
|
*
|
|
104
|
-
* A BRACKET-ONLY function is the opposite case and yields the
|
|
104
|
+
* A BRACKET-ONLY function is the opposite case and yields the service deliberately.
|
|
105
105
|
* `decideOnly` does, and naming the requirement is what turns *"call me inside a
|
|
106
106
|
* bracket"* from a runtime defect into a compile error — presence being the one
|
|
107
107
|
* direction the type system can decide here. So the test is what the function
|
|
@@ -117,8 +117,8 @@ export declare class ReadLedger extends ReadLedger_base {
|
|
|
117
117
|
* Fill in a recorded hop's head. Never called if the read does not complete.
|
|
118
118
|
*
|
|
119
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
|
|
121
|
-
* could not name it. `Hop` was exported and this was not — the same interface, two
|
|
120
|
+
* one, `ReadLedger` is exported, so a consumer holding the service holds this
|
|
121
|
+
* type and could not name it. `Hop` was exported and this was not — the same interface, two
|
|
122
122
|
* treatments.
|
|
123
123
|
*/
|
|
124
124
|
export type CloseHop = (head: Position) => Effect.Effect<void>;
|
|
@@ -1 +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;AAQxB,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,
|
|
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;AAQxB,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,eAmBE;CAAG;AAErC;;;;;;;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;AAoCxD;;;;;;;;;;;;;;;;;;;;;;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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Event production (codec edge-helper): turn a typed payload into a wire
|
|
3
|
-
* `DcbEvent`. Encode is `Schema.
|
|
4
|
-
* `CodecError`; any encryption transform runs here, *before* serialisation)
|
|
3
|
+
* `DcbEvent`. Encode is `Schema.encodeEffect(def.schema)` (a `SchemaError`
|
|
4
|
+
* becomes a `CodecError`; any encryption transform runs here, *before* serialisation)
|
|
5
5
|
* then `serialiser.serialise`. The wire `tags` are derived from the def's
|
|
6
6
|
* required `tagsFor` mapper (ADR-0006) — they are never hand-passed, so two
|
|
7
7
|
* command sites emitting the same event type cannot tag it differently. A mapper
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Context } from 'effect';
|
|
2
|
+
declare const MetadataSource_base: Context.ServiceClass<MetadataSource, "@kairos-es/codec/MetadataSource", {
|
|
3
|
+
readonly [x: string]: string;
|
|
4
|
+
}>;
|
|
5
|
+
export declare class MetadataSource extends MetadataSource_base {
|
|
6
|
+
}
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=MetadataSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetadataSource.d.ts","sourceRoot":"","sources":["../../src/MetadataSource.ts"],"names":[],"mappings":"AAsCA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;;;;AAEhC,qBAAa,cAAe,SAAQ,mBAEnC;CAAG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pluggable serialiser: the `Uint8Array` <-> value boundary applied
|
|
3
|
+
* *outside* `Schema`. Encode is `Schema.encodeEffect` then `serialise`; decode is
|
|
4
|
+
* `deserialise` then `Schema.decodeUnknownEffect`. Both codec paths read the same
|
|
5
|
+
* `Serializer` from context, so encode and decode cannot mismatch; swapping
|
|
6
|
+
* JSON for msgpack is a different layer with no schema or call-site changes.
|
|
7
|
+
*
|
|
8
|
+
* `deserialise` may throw synchronously (corrupt bytes): wrapping it as a
|
|
9
|
+
* `CodecError` is the *caller's* concern — every decode site wraps it in
|
|
10
|
+
* `Effect.try`. Keeping the service interface plain (no Effect) lets a layer be
|
|
11
|
+
* a trivial `Layer.succeed`.
|
|
12
|
+
*/
|
|
13
|
+
import { Context, Layer } from 'effect';
|
|
14
|
+
declare const Serializer_base: Context.ServiceClass<Serializer, "@kairos-es/codec/Serializer", {
|
|
15
|
+
readonly serialise: (v: unknown) => Uint8Array;
|
|
16
|
+
readonly deserialise: (b: Uint8Array) => unknown;
|
|
17
|
+
}>;
|
|
18
|
+
export declare class Serializer extends Serializer_base {
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The shipped default: JSON over UTF-8. `serialise` = `JSON.stringify` then
|
|
22
|
+
* `TextEncoder`; `deserialise` = `TextDecoder` then `JSON.parse` (which throws
|
|
23
|
+
* synchronously on corrupt bytes, wrapped to `CodecError` at the call site).
|
|
24
|
+
*/
|
|
25
|
+
export declare const SerializerDefault: Layer.Layer<Serializer>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=Serializer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Serializer.d.ts","sourceRoot":"","sources":["../../src/Serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;;wBAKf,CAAC,CAAC,EAAE,OAAO,KAAK,UAAU;0BACxB,CAAC,CAAC,EAAE,UAAU,KAAK,OAAO;;AAJpD,qBAAa,UAAW,SAAQ,eAME;CAAG;AAKrC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAMrD,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
declare const Uuid_base: Context.ServiceClass<Uuid, "@kairos-es/codec/Uuid", {
|
|
3
|
+
readonly generate: Effect.Effect<string>;
|
|
4
|
+
}>;
|
|
5
|
+
export declare class Uuid extends Uuid_base {
|
|
6
|
+
}
|
|
7
|
+
/** Live uuid generation via WebCrypto (no `node:crypto`). */
|
|
8
|
+
export declare const UuidLive: Layer.Layer<Uuid>;
|
|
9
|
+
/**
|
|
10
|
+
* A deterministic uuid layer for suites, wrapping core's shared
|
|
11
|
+
* `makeSequentialUuid` (the single source of truth for the counter scheme).
|
|
12
|
+
* Each BUILD of the layer makes a fresh generator, so it owns its own counter and
|
|
13
|
+
* yields a stable v4-shaped sequence `00000000-0000-4000-8000-<12 hex>` starting
|
|
14
|
+
* at `...000000000000`. A build is not one per `Effect.provide`: Effect memoises
|
|
15
|
+
* a layer across the provisions that share a memo map, so a nested second
|
|
16
|
+
* provision of this layer continues the counter rather than restarting it, and
|
|
17
|
+
* `Layer.fresh` is the way to ask for a restart. Generation is wrapped in `Effect.sync` because the
|
|
18
|
+
* generator advances its counter (a side effect on each call).
|
|
19
|
+
*/
|
|
20
|
+
export declare const UuidTest: Layer.Layer<Uuid>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=Uuid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Uuid.d.ts","sourceRoot":"","sources":["../../src/Uuid.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;;uBAIxB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;;AAF5C,qBAAa,IAAK,SAAQ,SAGE;CAAG;AAE/B,6DAA6D;AAC7D,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAErC,CAAA;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAGrC,CAAA"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `Decider`: the write side's unit of decision. It pairs the projections a
|
|
3
|
+
* command handler reads (its decision model) with an effectful `decide` step
|
|
4
|
+
* that turns the folded state plus a command into the events to append.
|
|
5
|
+
*
|
|
6
|
+
* This is PRODUCTION code and it lives beside the `buildDecisionModel` it
|
|
7
|
+
* drives. It began in `@kairos-es/testing` because the Given/When/Then surface
|
|
8
|
+
* was its first consumer, but nothing in it is test apparatus: every import
|
|
9
|
+
* resolves to this package or to `@kairos-es/core`, and a test framework is
|
|
10
|
+
* never one of them. That home was a real hazard rather than a tidiness
|
|
11
|
+
* complaint — `@kairos-es/testing` peers `@effect/vitest`, so a command handler
|
|
12
|
+
* typed against `Decider` (as this repo's own worked example is, in `src/` and
|
|
13
|
+
* not `test/`) named a package whose sibling module imports `expect` and `it` at
|
|
14
|
+
* top level, and a handler that actually RAN a command would have
|
|
15
|
+
* runtime-imported `runDecider` from it, loading a test framework into a
|
|
16
|
+
* production module graph. `@kairos-es/read/testing` documents that same hazard
|
|
17
|
+
* as the reason a subpath may add no peer; the difference here is that this is
|
|
18
|
+
* not a testing surface at all, so the fix is a home rather than a rule.
|
|
19
|
+
*
|
|
20
|
+
* `@kairos-es/testing` now imports these two symbols like any other consumer,
|
|
21
|
+
* and NO compatibility re-export is left behind there: nothing is published yet,
|
|
22
|
+
* so a re-export would be permanent clutter bought for no consumer, and it would
|
|
23
|
+
* preserve in that package's public surface exactly the coupling the move
|
|
24
|
+
* exists to remove. That is the argument ADR-0008 made when `retryOnConflict`
|
|
25
|
+
* moved to the package that owns the error it matches, and it holds here for the
|
|
26
|
+
* same reason — a write-side consumer depending on the wrong package is
|
|
27
|
+
* backwards, whichever direction the dependency points.
|
|
28
|
+
*
|
|
29
|
+
* `projections` is a *function of the command* because a DCB decision model is
|
|
30
|
+
* parametrised by the command's own identifiers (e.g. narrow to
|
|
31
|
+
* `course:{command.courseId}`), which keeps the consistency boundary — and the
|
|
32
|
+
* `failIfEventsMatch` guard the read produces — as tight as the decision
|
|
33
|
+
* actually needs. This mirrors dcb-events, where the command handler calls
|
|
34
|
+
* `buildDecisionModel(store, { ...projections built from command.data })`.
|
|
35
|
+
*
|
|
36
|
+
* `runDecider` runs the DCB command cycle against whatever `DcbEventStore` is in
|
|
37
|
+
* context: build the decision model for the command (a real `read` + decode +
|
|
38
|
+
* fold via `buildDecisionModel`), decide, then `append` the new events under the
|
|
39
|
+
* append condition the read produced. That IS the optimistic-concurrency
|
|
40
|
+
* contract, in production and in a scenario alike — which is also why the
|
|
41
|
+
* Given/When/Then surface built on it exercises the real fold and the real store
|
|
42
|
+
* rather than a mock. It is a free function rather than a `Context.Service`,
|
|
43
|
+
* so it is still not the "production command-runner service method" a retrying
|
|
44
|
+
* `Layer` would need (ADR-0008): the correct retry remains
|
|
45
|
+
* `retryOnConflict(runDecider(...))`, wrapping the whole cycle so a fresh read
|
|
46
|
+
* re-derives the condition.
|
|
47
|
+
*
|
|
48
|
+
* The `decide` step produces its events effectfully (they are encoded via
|
|
49
|
+
* `toEvent`, so it requires `Serializer | Uuid | R`) and may fail on its own
|
|
50
|
+
* error channel (a domain rule violation, e.g. "course already exists"); the
|
|
51
|
+
* append may fail with `AppendConditionFailed` if a concurrent writer moved the
|
|
52
|
+
* boundary, and decode may fail with `CodecError`. All surface on the error
|
|
53
|
+
* channel, so a caller handles all three the same way — and
|
|
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.
|
|
66
|
+
*/
|
|
67
|
+
import type { AppendConditionFailed, CompositeState, DcbEvent } from '@kairos-es/core';
|
|
68
|
+
import { DcbEventStore } from '@kairos-es/core';
|
|
69
|
+
import type { Array as Arr } from 'effect';
|
|
70
|
+
import { Effect } from 'effect';
|
|
71
|
+
import { type DecisionOutput, type DecisionResult } from './decision.js';
|
|
72
|
+
import { type RequirementOf } from './decisionModel.js';
|
|
73
|
+
import type { CodecError } from './errors.js';
|
|
74
|
+
import type { SliceProjection } from './projection.js';
|
|
75
|
+
import type { TaglessSliceInLedger } from './readLedger.js';
|
|
76
|
+
import type { Serializer } from './Serializer.js';
|
|
77
|
+
import type { Uuid } from './Uuid.js';
|
|
78
|
+
/**
|
|
79
|
+
* A decision unit: the projections whose folded state a command is decided
|
|
80
|
+
* against, and the `decide` step producing the events to append.
|
|
81
|
+
*
|
|
82
|
+
* `decide` may fail with a domain error `E` (a tagged error is idiomatic, so
|
|
83
|
+
* `@kairos-es/testing`'s `.thenError` can match its `_tag`). It receives the
|
|
84
|
+
* composed decision-model `state` and the `command`, and returns the non-empty
|
|
85
|
+
* batch of new events — effectfully, because each event is produced via
|
|
86
|
+
* `toEvent` (encode -> serialise), which requires `Serializer | Uuid | R`.
|
|
87
|
+
*
|
|
88
|
+
* The projection record is bounded by this package's `SliceProjection` (the
|
|
89
|
+
* schema-backed, state-erased bound for a heterogeneous record); the
|
|
90
|
+
* precise per-key state is recovered by core's shared `CompositeState`. The
|
|
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.
|
|
99
|
+
*/
|
|
100
|
+
export interface Decider<P extends Record<string, SliceProjection>, Command, E = never, R = never, O extends DecisionOutput = Arr.NonEmptyReadonlyArray<DcbEvent>> {
|
|
101
|
+
readonly projections: (command: Command) => P;
|
|
102
|
+
readonly decide: (state: CompositeState<P>, command: Command) => Effect.Effect<O, E, Serializer | Uuid | R>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build a `Decider` with all four of its type arguments INFERRED: the slice
|
|
106
|
+
* record `P` from what `projections` returns, the error union `E` and the extra
|
|
107
|
+
* requirement `R` from what `decide` returns. The command type is the one thing
|
|
108
|
+
* a caller still writes, once, as the annotation on the `projections` parameter
|
|
109
|
+
* — `Command` occurs only in parameter positions, so nothing else could supply
|
|
110
|
+
* it, and it is domain information (the command this decision is parametrised
|
|
111
|
+
* by) rather than plumbing.
|
|
112
|
+
*
|
|
113
|
+
* It adds no capability: the interface stays directly inhabitable and this
|
|
114
|
+
* returns exactly the object literal a caller would have written. What it
|
|
115
|
+
* changes is what an author must SPELL. An object literal annotated
|
|
116
|
+
* `Decider<P, Command, E>` has to state the whole slice record — a
|
|
117
|
+
* `ReturnType<typeof someProjection>` per key, a slice's state type being
|
|
118
|
+
* inferred by `projection().on().build()` and so having no name to write — plus
|
|
119
|
+
* the union of every error its decide branches can fail with. Dropping the
|
|
120
|
+
* annotation instead is not open to the author: without it `decide`'s `state`
|
|
121
|
+
* and `command` parameters have no contextual type and are implicitly `any`. So
|
|
122
|
+
* the cost landed hardest on the worked example, where some thirty lines of type
|
|
123
|
+
* plumbing sat between the reader and the three decisions that file exists to
|
|
124
|
+
* teach.
|
|
125
|
+
*
|
|
126
|
+
* `Eff` captures decide's WHOLE return type as one type parameter instead of
|
|
127
|
+
* inferring `E` and `R` from an `Effect<…, E, Serializer | Uuid | R>` shaped
|
|
128
|
+
* target, and that is the part that had to be got right rather than merely
|
|
129
|
+
* written down. A real `decide` returns a UNION of effects — one `Effect.fail`
|
|
130
|
+
* per rule it enforces, plus the produce path — and TypeScript infers a type
|
|
131
|
+
* parameter from a union source by choosing one constituent's candidate, not by
|
|
132
|
+
* unioning them. Against the shaped target `E` therefore came out as whichever
|
|
133
|
+
* branch won, and every other branch was then rejected against it. Inference to
|
|
134
|
+
* a NAKED type variable takes the union whole, which is why `Eff` is the
|
|
135
|
+
* parameter and `Effect.Error` / `Effect.Services` recover the two
|
|
136
|
+
* channels afterwards: those do distribute across a union of effects.
|
|
137
|
+
*
|
|
138
|
+
* The constraint on `Eff` is load-bearing twice over. It is what still refuses a
|
|
139
|
+
* `decide` that could produce an EMPTY batch, the one guarantee the shaped
|
|
140
|
+
* target used to give and the one `append` relies on. It is also what
|
|
141
|
+
* contextually types the produce path's `(event) => [event]` as a one-element
|
|
142
|
+
* TUPLE; unconstrained, that widens to `DcbEvent[]`, which is not a
|
|
143
|
+
* `NonEmptyReadonlyArray` and would fail at the call site.
|
|
144
|
+
*
|
|
145
|
+
* The single `as` is narrow and unavoidable: the declared return is a
|
|
146
|
+
* conditional over an unresolved `Eff`, so while `Eff` is still a type variable
|
|
147
|
+
* the checker cannot relate `(…) => Eff` to `(…) => Effect<…,
|
|
148
|
+
* Effect.Error<Eff>, …>`. The value is the two functions it was handed, so
|
|
149
|
+
* there is nothing for the cast to be wrong about.
|
|
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
|
+
*
|
|
167
|
+
* `R` excludes `Serializer | Uuid` because that is what `R` MEANS on `Decider`:
|
|
168
|
+
* the interface writes decide's requirement as `Serializer | Uuid | R`, so `R`
|
|
169
|
+
* is what the produce side needs BEYOND the codec's own two services. Leaving
|
|
170
|
+
* them in would state the same requirement twice and make an ordinary decider
|
|
171
|
+
* read as though it carried an extra one, though the set `runDecider` ends up
|
|
172
|
+
* asking for is identical either way. The exclusion cannot swallow a caller's
|
|
173
|
+
* own service by accident: a `Context.Service` class's requirement type carries
|
|
174
|
+
* its key as a literal `key` field, so only a service declared under `@kairos-es/codec/Uuid` or
|
|
175
|
+
* `@kairos-es/codec/Serializer` matches — and a service under that key IS that
|
|
176
|
+
* service, context being keyed by the same string at runtime.
|
|
177
|
+
*/
|
|
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.Error<Eff>, Exclude<Effect.Services<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.Error<Eff>, Exclude<Effect.Services<Eff>, Serializer | Uuid>, DecisionOutput>;
|
|
180
|
+
/**
|
|
181
|
+
* Run the full DCB command cycle for a decider against the ambient
|
|
182
|
+
* `DcbEventStore`: build the decision model (real read + decode + fold), decide,
|
|
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.
|
|
189
|
+
*
|
|
190
|
+
* The requirement is `DcbEventStore | Serializer | Uuid | R |
|
|
191
|
+
* RequirementOf<P>`: `Serializer` enters through both the decode
|
|
192
|
+
* (`buildDecisionModel`) and produce (`toEvent`) sides, `Uuid` through produce,
|
|
193
|
+
* the slices' `RequirementOf<P>` through decode, and `R` through produce. (For a
|
|
194
|
+
* def whose `Schema` carries a requirement, `RequirementOf<P>` and `R` are the
|
|
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.
|
|
204
|
+
*/
|
|
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>>;
|
|
206
|
+
//# sourceMappingURL=decider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decider.d.ts","sourceRoot":"","sources":["../../src/decider.ts"],"names":[],"mappings":"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"}
|