@kairos-es/codec 1.0.0-alpha.3 → 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 +27 -2
- package/dist/cjs/MetadataSource.js +10 -0
- package/dist/cjs/MetadataSource.js.map +1 -0
- 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/index.js +7 -0
- package/dist/cjs/index.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 +20 -5
- package/dist/cjs/readLedger.js.map +1 -1
- package/dist/cjs/registry.js.map +1 -1
- package/dist/cjs/toEvent.js +58 -4
- package/dist/cjs/toEvent.js.map +1 -1
- package/dist/dts-cjs/MetadataSource.d.ts +8 -0
- package/dist/dts-cjs/MetadataSource.d.ts.map +1 -0
- 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 → dts-cjs}/index.d.ts +4 -2
- package/dist/dts-cjs/index.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 +15 -4
- package/dist/dts-cjs/toEvent.d.ts.map +1 -0
- 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 +3 -0
- package/dist/esm/MetadataSource.js.map +1 -0
- 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/index.js +4 -2
- package/dist/esm/index.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 +21 -6
- package/dist/esm/readLedger.js.map +1 -1
- package/dist/esm/registry.js.map +1 -1
- package/dist/esm/toEvent.js +58 -5
- package/dist/esm/toEvent.js.map +1 -1
- package/package.json +12 -7
- package/src/MetadataSource.ts +43 -0
- 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/index.ts +4 -2
- package/src/precondition.ts +3 -3
- package/src/projection.ts +15 -20
- package/src/readLedger.ts +28 -12
- package/src/registry.ts +1 -1
- package/src/toEvent.ts +54 -7
- package/dist/dts/errors.d.ts.map +0 -1
- package/dist/dts/event.d.ts.map +0 -1
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/dts/projection.d.ts.map +0 -1
- package/dist/dts/toEvent.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}/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
|
@@ -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
|
|
@@ -60,8 +60,8 @@ import { CodecError } from './errors'
|
|
|
60
60
|
import type { Serializer } from './Serializer'
|
|
61
61
|
|
|
62
62
|
export const deserialiseThenDecode = <R>(
|
|
63
|
-
serializer: Context.
|
|
64
|
-
schema: Schema.
|
|
63
|
+
serializer: Context.Service.Shape<typeof Serializer>,
|
|
64
|
+
schema: Schema.Decoder<unknown, R>,
|
|
65
65
|
bytes: Uint8Array,
|
|
66
66
|
): Effect.Effect<unknown, CodecError, R> =>
|
|
67
67
|
Effect.try({
|
|
@@ -69,7 +69,7 @@ export const deserialiseThenDecode = <R>(
|
|
|
69
69
|
catch: (cause) => new CodecError({ reason: 'deserialise', cause }),
|
|
70
70
|
}).pipe(
|
|
71
71
|
Effect.flatMap((raw) =>
|
|
72
|
-
Schema.
|
|
72
|
+
Schema.decodeUnknownEffect(schema)(raw).pipe(
|
|
73
73
|
Effect.mapError((cause) => new CodecError({ reason: 'decode', cause })),
|
|
74
74
|
),
|
|
75
75
|
),
|
package/src/errors.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* never defects.
|
|
4
4
|
*
|
|
5
5
|
* `CodecError` covers every serialisation/`Schema` failure on the codec path
|
|
6
|
-
* (deserialise threw, `Schema.
|
|
6
|
+
* (deserialise threw, `Schema.decodeUnknownEffect`/`encodeEffect` produced a
|
|
7
|
+
* `SchemaError`, a
|
|
7
8
|
* wrong/absent encryption key surfaced through a `Schema` transform, or a
|
|
8
9
|
* `tagsFor` mapper threw while deriving an event's tags). It carries a
|
|
9
10
|
* human-readable `reason` and the underlying `cause` (exact-optional).
|
|
@@ -42,9 +43,10 @@ export class CodecError extends Data.TaggedError('CodecError')<{
|
|
|
42
43
|
*
|
|
43
44
|
* A GETTER rather than a second field, so `reason` stays the single declaration.
|
|
44
45
|
* It is visible because `Data.TaggedError`'s constructor is
|
|
45
|
-
* `super(args?.message, …)` followed by
|
|
46
|
-
* against `effect@
|
|
47
|
-
* nothing shadows this prototype accessor
|
|
46
|
+
* `super(args?.message, …)` followed by copying the args' own enumerable keys
|
|
47
|
+
* onto the instance (verified against `effect@4`'s `internal/core.ts`): the
|
|
48
|
+
* args above carry no `message`, so nothing shadows this prototype accessor
|
|
49
|
+
* with an own property.
|
|
48
50
|
*/
|
|
49
51
|
override get message(): string {
|
|
50
52
|
return this.reason
|
package/src/event.ts
CHANGED
|
@@ -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
|
*
|
|
@@ -42,22 +42,32 @@ export type TagsFor<A> = (payload: A) => Arr.NonEmptyReadonlyArray<Tag>
|
|
|
42
42
|
/**
|
|
43
43
|
* An event definition: its branded `type`, the `Schema` mapping its typed
|
|
44
44
|
* payload `A` to its wire encoding `I` (requiring `R`), and the `tagsFor` mapper
|
|
45
|
-
* that derives the event's tags from a decoded payload.
|
|
46
|
-
*
|
|
45
|
+
* that derives the event's tags from a decoded payload.
|
|
46
|
+
*
|
|
47
|
+
* `R` is ONE requirement for both directions, which is why the schema is a
|
|
48
|
+
* `Schema.Codec<A, I, R, R>` rather than leaving decoding and encoding services
|
|
49
|
+
* apart. A def is decoded on the read path and encoded by `toEvent`, so a
|
|
50
|
+
* consumer providing a def's services provides them for both; an application
|
|
51
|
+
* encryption transform needs its key either way. A struct whose two directions
|
|
52
|
+
* differ still fits, the codec being covariant in both, and `R` is then their
|
|
53
|
+
* union.
|
|
54
|
+
*
|
|
55
|
+
* `tagsFor` CONSUMES an `A`, so `EventDef` is invariant in `A` whatever the
|
|
56
|
+
* schema's own variance, and is therefore not declared `out A`.
|
|
47
57
|
*/
|
|
48
58
|
export interface EventDef<A = unknown, I = unknown, R = never> {
|
|
49
59
|
readonly type: EventType
|
|
50
|
-
readonly schema: Schema.
|
|
60
|
+
readonly schema: Schema.Codec<A, I, R, R>
|
|
51
61
|
readonly tagsFor: TagsFor<A>
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
/**
|
|
55
|
-
* The heterogeneous bound
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
65
|
+
* The heterogeneous bound for arrays/records of mixed defs, where the precise
|
|
66
|
+
* `A`/`I` differ per element. `tagsFor` erases to `(a: any) => …`, which is
|
|
67
|
+
* harmless: only the write path (`toEvent`) calls it, and the registry/decode
|
|
68
|
+
* path never does (stored events already carry tags).
|
|
59
69
|
*/
|
|
60
|
-
// biome-ignore lint/suspicious/noExplicitAny: the sanctioned heterogeneous bound
|
|
70
|
+
// biome-ignore lint/suspicious/noExplicitAny: the sanctioned heterogeneous bound; A/I differ per element and tagsFor consumes A, so any is the only sound erasure.
|
|
61
71
|
export type EventDefAny = EventDef<any, any, unknown>
|
|
62
72
|
|
|
63
73
|
/**
|
package/src/index.ts
CHANGED
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* on this path: the type is discovered at runtime, so no def was named to
|
|
18
18
|
* narrow it (see `registry.ts`).
|
|
19
19
|
*
|
|
20
|
-
* `toEvent(def, payload, { uuid?, occurredAt? })` produces a wire
|
|
21
|
-
* -> serialise, tags derived from `def.tagsFor`, clock + uuid from
|
|
20
|
+
* `toEvent(def, payload, { uuid?, occurredAt?, metadata? })` produces a wire
|
|
21
|
+
* event (encode -> serialise, tags derived from `def.tagsFor`, clock + uuid from
|
|
22
|
+
* services, metadata from the call or the optional ambient `MetadataSource`).
|
|
22
23
|
* Serialisation is a pluggable `Serializer` (JSON
|
|
23
24
|
* default). Encryption, when needed, is an *application*-authored `Schema`
|
|
24
25
|
* transform on a payload field; the codec only propagates its requirement and
|
|
@@ -134,6 +135,7 @@ export {
|
|
|
134
135
|
export { decodeSlices } from './decode'
|
|
135
136
|
export { CodecError, UnknownEventType } from './errors'
|
|
136
137
|
export { type EventDef, type EventDefAny, event, type TagsFor } from './event'
|
|
138
|
+
export { MetadataSource } from './MetadataSource'
|
|
137
139
|
export {
|
|
138
140
|
decideOnly,
|
|
139
141
|
type Precondition,
|
package/src/precondition.ts
CHANGED
|
@@ -148,7 +148,7 @@ import type { Uuid } from './Uuid'
|
|
|
148
148
|
* path meant to deny the input cheaply. Wrap such work in `Effect.suspend`.
|
|
149
149
|
*/
|
|
150
150
|
export interface Precondition<Id, Service, E, R> {
|
|
151
|
-
readonly tag: Context.
|
|
151
|
+
readonly tag: Context.Key<Id, Service>
|
|
152
152
|
readonly evaluate: Effect.Effect<Service, E, R>
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -243,7 +243,7 @@ export const decideOnly = <
|
|
|
243
243
|
* condition here. If that turns up in practice, a composing override is the
|
|
244
244
|
* additive answer and this hatch is not it.
|
|
245
245
|
*
|
|
246
|
-
* `Effect.
|
|
246
|
+
* `Effect.updateContext` over
|
|
247
247
|
* `Context.omit(AppendIsOwned)` is what lets it through the store's ownership
|
|
248
248
|
* refusal — the one route, and `AppendIsOwned.ts` owns why removing the marker is
|
|
249
249
|
* the shape rather than a second tag or a sanctioning flag.
|
|
@@ -275,7 +275,7 @@ export const unsafeAppend = (
|
|
|
275
275
|
// channel), so nothing here will fail to compile if this is deleted — what
|
|
276
276
|
// catches it is `test/appendOwnership.test.ts` in the source repository, not the
|
|
277
277
|
// type checker.
|
|
278
|
-
Effect.
|
|
278
|
+
Effect.updateContext(
|
|
279
279
|
store.append(events),
|
|
280
280
|
(context: Context.Context<never>) => Context.omit(AppendIsOwned)(context),
|
|
281
281
|
),
|
package/src/projection.ts
CHANGED
|
@@ -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.
|
|
@@ -35,7 +35,7 @@ import type { EventDef } from './event'
|
|
|
35
35
|
*/
|
|
36
36
|
export interface SliceProjection<out S = unknown, out R = unknown>
|
|
37
37
|
extends AnyProjection<S> {
|
|
38
|
-
readonly schemas: ReadonlyMap<string, Schema.
|
|
38
|
+
readonly schemas: ReadonlyMap<string, Schema.Decoder<unknown, R>>
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/** The fluent builder: accumulate `.on(def, fold)` steps, then `.build()`. */
|
|
@@ -75,7 +75,7 @@ export const projection = <S>(initialState: S): ProjectionBuilder<S, never> => {
|
|
|
75
75
|
// threading `R` means the implementation states the same widening the
|
|
76
76
|
// `ProjectionBuilder` interface promises, and `on` returns without a cast.
|
|
77
77
|
const make = <R>(
|
|
78
|
-
schemas: ReadonlyMap<string, Schema.
|
|
78
|
+
schemas: ReadonlyMap<string, Schema.Decoder<unknown, R>>,
|
|
79
79
|
folds: ReadonlyMap<string, (state: S, payload: unknown) => S>,
|
|
80
80
|
): ProjectionBuilder<S, R> => ({
|
|
81
81
|
on: <A, I, R2>(
|
|
@@ -101,25 +101,20 @@ export const projection = <S>(initialState: S): ProjectionBuilder<S, never> => {
|
|
|
101
101
|
// The entry type is annotated `R | R2` rather than inferred, and that
|
|
102
102
|
// annotation is what carries the requirement forward — `build` reads `R`
|
|
103
103
|
// straight off this map, so `RequirementOf` ultimately reads it from here.
|
|
104
|
-
// Widening the copied `R` entries into it costs nothing: `Schema`
|
|
105
|
-
// declared `<
|
|
104
|
+
// Widening the copied `R` entries into it costs nothing: `Schema.Decoder`
|
|
105
|
+
// is declared `<out T, out RD>` upstream, hence covariant in its
|
|
106
106
|
// requirement.
|
|
107
|
-
const nextSchemas = new Map<
|
|
108
|
-
|
|
109
|
-
Schema.Schema<unknown, unknown, R | R2>
|
|
110
|
-
>(schemas)
|
|
111
|
-
// Erasing `A`/`I` is the one unavoidable cast, and `as unknown as` is what
|
|
112
|
-
// it takes: `Schema` is INVARIANT in both, so `Schema<A, I, R2>` and
|
|
113
|
-
// `Schema<unknown, unknown, R2>` are unrelated in either direction. It is
|
|
114
|
-
// sound because the payload is only ever read back through `evolve`, which
|
|
115
|
-
// re-narrows by event type via the matching `fold`. The target names `R2`
|
|
116
|
-
// and not `never` because `R2` is what the def actually requires — an
|
|
117
|
-
// `Encrypted` field's `CipherKey` is not nothing — so the cast erases only
|
|
118
|
-
// what it has to and states the rest truthfully.
|
|
119
|
-
nextSchemas.set(
|
|
120
|
-
def.type,
|
|
121
|
-
def.schema as unknown as Schema.Schema<unknown, unknown, R2>,
|
|
107
|
+
const nextSchemas = new Map<string, Schema.Decoder<unknown, R | R2>>(
|
|
108
|
+
schemas,
|
|
122
109
|
)
|
|
110
|
+
// Erasing `A`/`I` needs no cast: a `Schema.Codec` is covariant in both, so
|
|
111
|
+
// `Codec<A, I, R2, R2>` is already a `Decoder<unknown, R2>`. The erasure is
|
|
112
|
+
// sound because the payload is only ever read back through `evolve`, which
|
|
113
|
+
// re-narrows by event type via the matching `fold`. What survives it is
|
|
114
|
+
// `R2`, the def's DECODING requirement — an `Encrypted` field's `CipherKey`
|
|
115
|
+
// is not nothing — and that is the half a slice ever spends, since a slice
|
|
116
|
+
// decodes and never encodes.
|
|
117
|
+
nextSchemas.set(def.type, def.schema)
|
|
123
118
|
const nextFolds = new Map(folds)
|
|
124
119
|
// Contravariance in the payload parameter, so this one is a cast too: the
|
|
125
120
|
// map holds folds over `unknown`, and `evolve` supplies the payload the
|
package/src/readLedger.ts
CHANGED
|
@@ -73,6 +73,7 @@ import {
|
|
|
73
73
|
ORIGIN,
|
|
74
74
|
servableQueryFault,
|
|
75
75
|
unionQueries,
|
|
76
|
+
unknownQueryKeyFault,
|
|
76
77
|
} from '@kairos-es/core'
|
|
77
78
|
import { Context, Data, Effect, Layer, Option, Ref } from 'effect'
|
|
78
79
|
|
|
@@ -97,7 +98,7 @@ export interface Hop {
|
|
|
97
98
|
* requirement channel nothing is exactly what makes this change additive.
|
|
98
99
|
* `modelAtHead` is that path and is why the rule exists.
|
|
99
100
|
*
|
|
100
|
-
* A BRACKET-ONLY function is the opposite case and yields the
|
|
101
|
+
* A BRACKET-ONLY function is the opposite case and yields the service deliberately.
|
|
101
102
|
* `decideOnly` does, and naming the requirement is what turns *"call me inside a
|
|
102
103
|
* bracket"* from a runtime defect into a compile error — presence being the one
|
|
103
104
|
* direction the type system can decide here. So the test is what the function
|
|
@@ -107,7 +108,7 @@ export interface Hop {
|
|
|
107
108
|
* Recording is two-phase, and `record`'s own doc below owns why and what shape the
|
|
108
109
|
* second phase takes.
|
|
109
110
|
*/
|
|
110
|
-
export class ReadLedger extends Context.
|
|
111
|
+
export class ReadLedger extends Context.Service<
|
|
111
112
|
ReadLedger,
|
|
112
113
|
{
|
|
113
114
|
/**
|
|
@@ -126,14 +127,14 @@ export class ReadLedger extends Context.Tag('@kairos-es/codec/ReadLedger')<
|
|
|
126
127
|
readonly record: (query: Query) => Effect.Effect<CloseHop>
|
|
127
128
|
readonly hops: Effect.Effect<ReadonlyArray<Hop>>
|
|
128
129
|
}
|
|
129
|
-
>() {}
|
|
130
|
+
>()('@kairos-es/codec/ReadLedger') {}
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
133
|
* Fill in a recorded hop's head. Never called if the read does not complete.
|
|
133
134
|
*
|
|
134
135
|
* On the package's public surface because it is already ON it: `record` returns
|
|
135
|
-
* one, `ReadLedger` is exported, so a consumer holding the
|
|
136
|
-
* could not name it. `Hop` was exported and this was not — the same interface, two
|
|
136
|
+
* one, `ReadLedger` is exported, so a consumer holding the service holds this
|
|
137
|
+
* type and could not name it. `Hop` was exported and this was not — the same interface, two
|
|
137
138
|
* treatments.
|
|
138
139
|
*/
|
|
139
140
|
export type CloseHop = (head: Position) => Effect.Effect<void>
|
|
@@ -302,7 +303,7 @@ export const makeReadLedger: Effect.Effect<{
|
|
|
302
303
|
*/
|
|
303
304
|
const withLedger = <A, E = never>(
|
|
304
305
|
absent: A,
|
|
305
|
-
f: (ledger: Context.
|
|
306
|
+
f: (ledger: Context.Service.Shape<typeof ReadLedger>) => Effect.Effect<A, E>,
|
|
306
307
|
): Effect.Effect<A, E> =>
|
|
307
308
|
Effect.flatMap(Effect.serviceOption(ReadLedger), (ledger) =>
|
|
308
309
|
Option.isNone(ledger) ? Effect.succeed(absent) : f(ledger.value),
|
|
@@ -329,14 +330,29 @@ const taglessKeys = (
|
|
|
329
330
|
)
|
|
330
331
|
.map(([key]) => key)
|
|
331
332
|
|
|
332
|
-
/**
|
|
333
|
+
/**
|
|
334
|
+
* Would recording this record's query leave the accumulated union unservable in
|
|
335
|
+
* its SHAPE — the one fault a slice's tags can fix, and so the one this ledger
|
|
336
|
+
* reports?
|
|
337
|
+
*
|
|
338
|
+
* An unknown key anywhere in the union answers no. It is not a tagless slice and no
|
|
339
|
+
* slice's tags would fix it, and the store refuses it as a defect naming the key at
|
|
340
|
+
* the read of the hop that carries it, so an earlier hop's key has already been
|
|
341
|
+
* refused once by the time a later record is judged. Judged here it would surface
|
|
342
|
+
* as this ledger's error, on the error channel, naming a slice with nothing wrong
|
|
343
|
+
* with it — and a hop is recorded before its read runs, so an earlier hop's key
|
|
344
|
+
* would go on doing that to every clean read after it.
|
|
345
|
+
*/
|
|
333
346
|
const unionWouldBeUnservable = (
|
|
334
347
|
existing: ReadonlyArray<Hop>,
|
|
335
348
|
incoming: Query,
|
|
336
|
-
): boolean =>
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
349
|
+
): boolean => {
|
|
350
|
+
const union = unionQueries([...existing.map((h) => h.query), incoming])
|
|
351
|
+
return (
|
|
352
|
+
unknownQueryKeyFault(union) === undefined &&
|
|
353
|
+
servableQueryFault(union) !== undefined
|
|
354
|
+
)
|
|
355
|
+
}
|
|
340
356
|
|
|
341
357
|
/**
|
|
342
358
|
* Refuse a read that would leave the bracket's accumulated union unservable,
|
|
@@ -389,6 +405,6 @@ export const ambientLayeredCondition: Effect.Effect<
|
|
|
389
405
|
Option.Option<AppendCondition>
|
|
390
406
|
> = withLedger(Option.none<AppendCondition>(), (ledger) =>
|
|
391
407
|
Effect.map(ledger.hops, (hops) =>
|
|
392
|
-
Option.
|
|
408
|
+
Option.fromNullishOr(deriveLayeredCondition(hops)),
|
|
393
409
|
),
|
|
394
410
|
)
|
package/src/registry.ts
CHANGED
|
@@ -53,7 +53,7 @@ type RequirementOfDefs<Defs extends ReadonlyArray<EventDefAny>> =
|
|
|
53
53
|
export const makeRegistry = <Defs extends ReadonlyArray<EventDefAny>>(
|
|
54
54
|
defs: Defs,
|
|
55
55
|
): Registry<RequirementOfDefs<Defs>> => {
|
|
56
|
-
const map = new Map<string, Schema.
|
|
56
|
+
const map = new Map<string, Schema.Decoder<unknown, unknown>>()
|
|
57
57
|
for (const def of defs) {
|
|
58
58
|
if (map.has(def.type)) {
|
|
59
59
|
throw new Error(`duplicate event type '${def.type}' in registry`)
|
package/src/toEvent.ts
CHANGED
|
@@ -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
|
|
@@ -10,11 +10,19 @@
|
|
|
10
10
|
* comes from `options.uuid` or the `Uuid` service; `occurredAt` from
|
|
11
11
|
* `options.occurredAt` or the Effect clock (`DateTime.now`), keeping `core`
|
|
12
12
|
* clock- and RNG-free while staying overridable for backdated imports.
|
|
13
|
+
* `metadata` comes from `options.metadata` or the OPTIONAL ambient
|
|
14
|
+
* `MetadataSource` service, and is `{}` when neither supplies it (ADR-0017). A
|
|
15
|
+
* bag core's `Metadata` schema refuses, such as one holding a value that is not a
|
|
16
|
+
* string, fails on the `CodecError` channel (`reason: 'metadata'`), whichever of
|
|
17
|
+
* the two supplied it. It never reaches `tagsFor`, the
|
|
18
|
+
* event's schema or the serialiser, so it can enter neither the tag junction
|
|
19
|
+
* table nor the encryption seam.
|
|
13
20
|
*/
|
|
14
|
-
import type
|
|
15
|
-
import { DateTime, Effect, Schema } from 'effect'
|
|
21
|
+
import { type DcbEvent, Metadata } from '@kairos-es/core'
|
|
22
|
+
import { DateTime, Effect, Option, Schema } from 'effect'
|
|
16
23
|
import { CodecError } from './errors'
|
|
17
24
|
import type { EventDef } from './event'
|
|
25
|
+
import { MetadataSource } from './MetadataSource'
|
|
18
26
|
import { Serializer } from './Serializer'
|
|
19
27
|
import { Uuid } from './Uuid'
|
|
20
28
|
|
|
@@ -26,7 +34,10 @@ import { Uuid } from './Uuid'
|
|
|
26
34
|
* wire-envelope fields (the query surface), so a `tagsFor` mapper may only key
|
|
27
35
|
* off cleartext payload fields (ADR-0006). `uuid` / `occurredAt` are
|
|
28
36
|
* exact-optional overrides; absent, they come from the `Uuid` service and the
|
|
29
|
-
* Effect clock.
|
|
37
|
+
* Effect clock. `metadata` is an exact-optional override too; absent, it comes
|
|
38
|
+
* from the ambient `MetadataSource` if one is provided and is `{}` otherwise.
|
|
39
|
+
* The ambient read adds nothing to the requirement channel. A bag core's
|
|
40
|
+
* `Metadata` schema refuses fails as a `CodecError` with `reason: 'metadata'`.
|
|
30
41
|
*/
|
|
31
42
|
export const toEvent = <A, I, R>(
|
|
32
43
|
def: EventDef<A, I, R>,
|
|
@@ -34,10 +45,45 @@ export const toEvent = <A, I, R>(
|
|
|
34
45
|
options?: {
|
|
35
46
|
readonly uuid?: string
|
|
36
47
|
readonly occurredAt?: DateTime.Utc
|
|
48
|
+
readonly metadata?: Metadata
|
|
37
49
|
},
|
|
38
50
|
): Effect.Effect<DcbEvent, CodecError, Serializer | Uuid | R> =>
|
|
39
51
|
Effect.gen(function* () {
|
|
40
|
-
//
|
|
52
|
+
// Resolve metadata before anything else. Per-call wins, ambient fills,
|
|
53
|
+
// absent yields `{}`. `serviceOption` keeps `MetadataSource` out of the
|
|
54
|
+
// requirement channel.
|
|
55
|
+
//
|
|
56
|
+
// The bag is then CHECKED against core's `Metadata` schema, whichever arm
|
|
57
|
+
// supplied it. The type holds a typed caller to a bag of strings, but an
|
|
58
|
+
// `any` or untyped JavaScript gets past it, and the engines then disagree
|
|
59
|
+
// about what they stored: the cross-engine parity of this field rests on its
|
|
60
|
+
// values being strings, and this is the one place a value is refused. It is
|
|
61
|
+
// refused HERE, at write, and never on read: a read-side check would make an
|
|
62
|
+
// event the store had accepted unreadable for ever. A bad bag is the
|
|
63
|
+
// caller's mistake, so it fails on the `CodecError` channel exactly as a
|
|
64
|
+
// throwing `tagsFor` does, never as a defect. Checking consumes no sequence,
|
|
65
|
+
// so placing it above the tag derivation leaves the property below intact.
|
|
66
|
+
//
|
|
67
|
+
// The bag is COPIED, never stamped by reference: an ambient bag would
|
|
68
|
+
// otherwise be ONE object shared by every event of a request, and the
|
|
69
|
+
// caller's own object would be shared with the event it built, so a mutation
|
|
70
|
+
// of either before the append would rewrite the other. (After the append no
|
|
71
|
+
// store shares anything with its caller; that is each store's guarantee.)
|
|
72
|
+
const given: unknown =
|
|
73
|
+
options?.metadata === undefined
|
|
74
|
+
? Option.getOrElse(
|
|
75
|
+
yield* Effect.serviceOption(MetadataSource),
|
|
76
|
+
(): Metadata => ({}),
|
|
77
|
+
)
|
|
78
|
+
: options.metadata
|
|
79
|
+
const metadata: Metadata = {
|
|
80
|
+
...(yield* Schema.decodeUnknownEffect(Metadata)(given).pipe(
|
|
81
|
+
Effect.mapError(
|
|
82
|
+
(cause) => new CodecError({ reason: 'metadata', cause }),
|
|
83
|
+
),
|
|
84
|
+
)),
|
|
85
|
+
}
|
|
86
|
+
// Derive tags next. A mapper is app code that can throw (e.g. `Tag.make`
|
|
41
87
|
// rejects an ill-formed `key:value`), and the codec's contract is that such
|
|
42
88
|
// failures are values on the `CodecError` channel, never defects (errors.ts).
|
|
43
89
|
// Deriving before the uuid/clock effects also means a bad tag fails without
|
|
@@ -47,7 +93,7 @@ export const toEvent = <A, I, R>(
|
|
|
47
93
|
catch: (cause) => new CodecError({ reason: 'tags', cause }),
|
|
48
94
|
})
|
|
49
95
|
const serializer = yield* Serializer
|
|
50
|
-
const encoded = yield* Schema.
|
|
96
|
+
const encoded = yield* Schema.encodeEffect(def.schema)(payload).pipe(
|
|
51
97
|
Effect.mapError((cause) => new CodecError({ reason: 'encode', cause })),
|
|
52
98
|
)
|
|
53
99
|
const data = serializer.serialise(encoded)
|
|
@@ -59,5 +105,6 @@ export const toEvent = <A, I, R>(
|
|
|
59
105
|
tags,
|
|
60
106
|
uuid,
|
|
61
107
|
occurredAt,
|
|
108
|
+
metadata,
|
|
62
109
|
}
|
|
63
110
|
})
|
package/dist/dts/errors.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAeA,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;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;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"}
|
package/dist/dts/event.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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;;;;;GAKG;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,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACvC,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"}
|
package/dist/dts/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqHG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EACL,QAAQ,EACR,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,WAAW,EACX,SAAS,EACT,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAA;AAC9E,OAAO,EACL,UAAU,EACV,KAAK,YAAY,EACjB,QAAQ,EACR,YAAY,GACb,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,KAAK,iBAAiB,EACtB,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,GAAG,EACR,eAAe,EACf,UAAU,EACV,oBAAoB,GACrB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
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,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;CAC1E;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,CAiGzE,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"toEvent.d.ts","sourceRoot":"","sources":["../../src/toEvent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE7B;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC7B,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACtB,SAAS,CAAC,EACV,UAAU;IACR,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAA;CACnC,KACA,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,CAAC,CAyBxD,CAAA"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|