@kairos-es/core 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 +3 -3
- package/dist/cjs/AppendCondition.js +1 -3
- package/dist/cjs/AppendCondition.js.map +1 -1
- package/dist/cjs/AppendIsOwned.js +6 -6
- package/dist/cjs/AppendIsOwned.js.map +1 -1
- package/dist/cjs/ContractStore.js +13 -4
- package/dist/cjs/ContractStore.js.map +1 -1
- package/dist/cjs/DcbEventStore.js +1 -1
- package/dist/cjs/DcbEventStore.js.map +1 -1
- package/dist/cjs/Event.js +87 -11
- package/dist/cjs/Event.js.map +1 -1
- package/dist/cjs/Projection.js +15 -9
- package/dist/cjs/Projection.js.map +1 -1
- package/dist/cjs/Query.js +59 -3
- package/dist/cjs/Query.js.map +1 -1
- package/dist/cjs/SubscribeMachine.js +20 -22
- package/dist/cjs/SubscribeMachine.js.map +1 -1
- package/dist/cjs/inMemory.js +113 -43
- package/dist/cjs/inMemory.js.map +1 -1
- package/dist/cjs/retryOnConflict.js +4 -10
- package/dist/cjs/retryOnConflict.js.map +1 -1
- package/dist/cjs/testing.js +2 -2
- package/dist/cjs/testing.js.map +1 -1
- package/dist/{dts → dts-cjs}/AppendCondition.d.ts +5 -7
- package/dist/{dts → dts-cjs}/AppendCondition.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/AppendIsOwned.d.ts +6 -6
- package/dist/{dts → dts-cjs}/AppendIsOwned.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/ContractStore.d.ts +7 -4
- package/dist/dts-cjs/ContractStore.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/DcbEventStore.d.ts +2 -2
- package/dist/{dts → dts-cjs}/DcbEventStore.d.ts.map +1 -1
- package/dist/dts-cjs/Event.d.ts +174 -0
- package/dist/dts-cjs/Event.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/Projection.d.ts +1 -1
- package/dist/{dts → dts-cjs}/Projection.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/Query.d.ts +19 -5
- package/dist/dts-cjs/Query.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/SubscribeMachine.d.ts +11 -15
- package/dist/dts-cjs/SubscribeMachine.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/inMemory.d.ts.map +1 -1
- package/dist/{dts → dts-cjs}/index.d.ts +1 -1
- package/dist/dts-cjs/package.json +3 -0
- package/dist/{dts → dts-cjs}/retryOnConflict.d.ts +1 -7
- package/dist/dts-cjs/retryOnConflict.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/testing.d.ts +1 -1
- package/dist/dts-esm/AppendCondition.d.ts +21 -0
- package/dist/dts-esm/AppendCondition.d.ts.map +1 -0
- package/dist/dts-esm/AppendIsOwned.d.ts +78 -0
- package/dist/dts-esm/AppendIsOwned.d.ts.map +1 -0
- package/dist/dts-esm/ContractStore.d.ts +62 -0
- package/dist/dts-esm/ContractStore.d.ts.map +1 -0
- package/dist/dts-esm/DcbEventStore.d.ts +64 -0
- package/dist/dts-esm/DcbEventStore.d.ts.map +1 -0
- package/dist/dts-esm/Event.d.ts +174 -0
- package/dist/dts-esm/Event.d.ts.map +1 -0
- package/dist/dts-esm/Projection.d.ts +124 -0
- package/dist/dts-esm/Projection.d.ts.map +1 -0
- package/dist/dts-esm/Query.d.ts +180 -0
- package/dist/dts-esm/Query.d.ts.map +1 -0
- package/dist/dts-esm/SubscribeMachine.d.ts +175 -0
- package/dist/dts-esm/SubscribeMachine.d.ts.map +1 -0
- package/dist/dts-esm/errors.d.ts +17 -0
- package/dist/dts-esm/errors.d.ts.map +1 -0
- package/dist/dts-esm/inMemory.d.ts +76 -0
- package/dist/dts-esm/inMemory.d.ts.map +1 -0
- package/dist/dts-esm/index.d.ts +40 -0
- package/dist/dts-esm/index.d.ts.map +1 -0
- package/dist/dts-esm/package.json +3 -0
- package/dist/dts-esm/retryOnConflict.d.ts +64 -0
- package/dist/dts-esm/retryOnConflict.d.ts.map +1 -0
- package/dist/dts-esm/testing.d.ts +39 -0
- package/dist/dts-esm/testing.d.ts.map +1 -0
- package/dist/esm/AppendCondition.js +1 -3
- package/dist/esm/AppendCondition.js.map +1 -1
- package/dist/esm/AppendIsOwned.js +6 -6
- package/dist/esm/AppendIsOwned.js.map +1 -1
- package/dist/esm/ContractStore.js +13 -4
- package/dist/esm/ContractStore.js.map +1 -1
- package/dist/esm/DcbEventStore.js +1 -1
- package/dist/esm/DcbEventStore.js.map +1 -1
- package/dist/esm/Event.js +87 -11
- package/dist/esm/Event.js.map +1 -1
- package/dist/esm/Projection.js +15 -9
- package/dist/esm/Projection.js.map +1 -1
- package/dist/esm/Query.js +56 -1
- package/dist/esm/Query.js.map +1 -1
- package/dist/esm/SubscribeMachine.js +21 -23
- package/dist/esm/SubscribeMachine.js.map +1 -1
- package/dist/esm/inMemory.js +114 -44
- package/dist/esm/inMemory.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/retryOnConflict.js +4 -10
- package/dist/esm/retryOnConflict.js.map +1 -1
- package/dist/esm/testing.js +2 -2
- package/dist/esm/testing.js.map +1 -1
- package/package.json +21 -11
- package/src/AppendCondition.ts +1 -1
- package/src/AppendIsOwned.ts +7 -7
- package/src/ContractStore.ts +25 -8
- package/src/DcbEventStore.ts +3 -3
- package/src/Event.ts +108 -11
- package/src/Projection.ts +19 -8
- package/src/Query.ts +74 -1
- package/src/SubscribeMachine.ts +38 -31
- package/src/inMemory.ts +123 -51
- package/src/index.ts +1 -1
- package/src/retryOnConflict.ts +6 -11
- package/src/testing.ts +2 -2
- package/testing/package.json +1 -1
- package/dist/dts/ContractStore.d.ts.map +0 -1
- package/dist/dts/Event.d.ts +0 -104
- package/dist/dts/Event.d.ts.map +0 -1
- package/dist/dts/Query.d.ts.map +0 -1
- package/dist/dts/SubscribeMachine.d.ts.map +0 -1
- package/dist/dts/retryOnConflict.d.ts.map +0 -1
- /package/dist/{dts → dts-cjs}/errors.d.ts +0 -0
- /package/dist/{dts → dts-cjs}/errors.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/inMemory.d.ts +0 -0
- /package/dist/{dts → dts-cjs}/index.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/testing.d.ts.map +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core domain types for the DCB event log: branded scalars and the wire
|
|
3
|
+
* `DcbEvent`/`SequencedEvent` shapes.
|
|
4
|
+
*
|
|
5
|
+
* `data` is opaque bytes at the store layer; the codec (`@kairos-es/codec`) turns typed
|
|
6
|
+
* payloads into these bytes. Ordering is by `position` alone; `occurredAt` is
|
|
7
|
+
* producer-supplied domain time and is informational only.
|
|
8
|
+
*/
|
|
9
|
+
import { Schema } from 'effect';
|
|
10
|
+
/**
|
|
11
|
+
* A position in the single global event log: a branded, non-negative `bigint`.
|
|
12
|
+
*
|
|
13
|
+
* The log is 64-bit (`bigserial` in Postgres) and exceeds JS `number` safety at
|
|
14
|
+
* 2^53, so positions are a `bigint`. They are compared, never arithmetic'd.
|
|
15
|
+
* Encoded, a position is a decimal string (JSON-safe, `String(b)`); decoded, a
|
|
16
|
+
* `bigint`. `0n` is the "before the first event" sentinel used by `after` and by
|
|
17
|
+
* an empty store's head.
|
|
18
|
+
*
|
|
19
|
+
* The string-to-`bigint` step is hand-built rather than Effect 4's
|
|
20
|
+
* `Schema.BigIntFromString`, because that schema accepts only `^-?\d+$` and so
|
|
21
|
+
* refuses inputs the encoded side has always admitted — `" 5"`, `"+5"`,
|
|
22
|
+
* `"0x10"`. The decode here is the global `BigInt(s)` behind a blank-string
|
|
23
|
+
* refusal, which is `BigInt.fromString` and is exactly what Effect 3's
|
|
24
|
+
* `Schema.BigInt` ran, so a position string any earlier release decoded still
|
|
25
|
+
* decodes to the same value. The non-negative check then runs on the `bigint`,
|
|
26
|
+
* as it always did.
|
|
27
|
+
*/
|
|
28
|
+
export declare const Position: Schema.brand<Schema.decodeTo<Schema.BigInt, Schema.String, never, never>, "Position">;
|
|
29
|
+
export type Position = typeof Position.Type;
|
|
30
|
+
/** The sentinel position meaning "before the first event". */
|
|
31
|
+
export declare const ORIGIN: Position;
|
|
32
|
+
/**
|
|
33
|
+
* A business identifier attached to an event, following the `key:value`
|
|
34
|
+
* convention (e.g. `student:s1`). Tags replace the per-stream id of classic
|
|
35
|
+
* event sourcing; a query matches an event when the event carries all of an
|
|
36
|
+
* item's tags (superset test).
|
|
37
|
+
*/
|
|
38
|
+
export declare const Tag: Schema.brand<Schema.String, "Tag">;
|
|
39
|
+
export type Tag = typeof Tag.Type;
|
|
40
|
+
/**
|
|
41
|
+
* A reusable tag identity: a `key` plus a `.make(value)` that composes the
|
|
42
|
+
* branded `Tag` `` `${key}:${value}` `` through core's `Tag` schema. Define an
|
|
43
|
+
* identity once (`const CourseId = tagIdentity('course')`) and reuse
|
|
44
|
+
* `CourseId.make(id)` everywhere that entity is tagged. The `key` is exposed for
|
|
45
|
+
* key-aware tooling (e.g. a tag-vocabulary index, a future retag pass).
|
|
46
|
+
*
|
|
47
|
+
* The same constructor authors an event's tags (in a codec `tagsFor` mapper,
|
|
48
|
+
* ADR-0006) and a query's tags, so the `key:value` string a mapper writes and
|
|
49
|
+
* the string a query matches are produced by one function and cannot drift. It
|
|
50
|
+
* lives here beside `Tag` — pure (no clock, no RNG, no effect) — so it is
|
|
51
|
+
* reachable from every tag-authoring site, including core's own query
|
|
52
|
+
* constructors.
|
|
53
|
+
*
|
|
54
|
+
* `key` and `value` are each a single colon-free atom: the `key:value` grammar
|
|
55
|
+
* splits on the first colon, so both are validated to be non-empty and contain
|
|
56
|
+
* no `:` — `key` once at construction, `value` on each `make`. This keeps
|
|
57
|
+
* composition unambiguous and catches the double-prefix footgun loudly:
|
|
58
|
+
* `CourseId.make('course:123')` throws rather than silently yielding
|
|
59
|
+
* `course:course:123`. Raw `Tag.make` remains the escape hatch for a value that
|
|
60
|
+
* must itself contain a colon.
|
|
61
|
+
*/
|
|
62
|
+
export interface TagIdentity {
|
|
63
|
+
readonly key: string;
|
|
64
|
+
readonly make: (value: string) => Tag;
|
|
65
|
+
}
|
|
66
|
+
export declare const tagIdentity: (key: string) => TagIdentity;
|
|
67
|
+
/**
|
|
68
|
+
* A read cap: a branded non-negative integer bounding how many events a `read`
|
|
69
|
+
* returns. `0` returns none; an absent limit returns all matching events.
|
|
70
|
+
*
|
|
71
|
+
* The bound lives in the type, not merely in prose: a negative or non-integer
|
|
72
|
+
* limit is a programming error, so it is a DEFECT at the store boundary — the
|
|
73
|
+
* store validates through this schema and dies rather than surfacing it on the
|
|
74
|
+
* error channel (`read` keeps `E = never`). Constructed at call sites with
|
|
75
|
+
* `ReadLimit.make(n)`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const ReadLimit: Schema.brand<Schema.Int, "ReadLimit">;
|
|
78
|
+
export type ReadLimit = typeof ReadLimit.Type;
|
|
79
|
+
/**
|
|
80
|
+
* An event type name, a branded non-empty string. Versioning is by convention
|
|
81
|
+
* in the name (e.g. `course-registered.v2`); only non-emptiness is enforced.
|
|
82
|
+
*/
|
|
83
|
+
export declare const EventType: Schema.brand<Schema.String, "EventType">;
|
|
84
|
+
export type EventType = typeof EventType.Type;
|
|
85
|
+
/**
|
|
86
|
+
* An event's metadata: a flat bag of strings carrying the circumstances under
|
|
87
|
+
* which the event was RECORDED, never anything the business asserts (ADR-0017).
|
|
88
|
+
* Payload is what happened; metadata is the context of the recording — a
|
|
89
|
+
* `traceparent`, a correlation or causation id, a deployment revision.
|
|
90
|
+
*
|
|
91
|
+
* Values are strings, not `unknown`, for cross-engine parity rather than taste:
|
|
92
|
+
* both SQL engines carry the bag as a JSON document, and `JSON.stringify` drops
|
|
93
|
+
* `undefined`, throws on `bigint` and turns `NaN` into `null`, none of which the
|
|
94
|
+
* in-memory oracle does.
|
|
95
|
+
*
|
|
96
|
+
* The key schema carries no refinement, deliberately: a refinement on a
|
|
97
|
+
* `Schema.Record` key acts as a FILTER on decode, silently dropping a key that
|
|
98
|
+
* fails it rather than failing the decode. Constraining keys, if ever wanted,
|
|
99
|
+
* has to be a validating step of its own.
|
|
100
|
+
*
|
|
101
|
+
* Two engine limits a consumer must know, because the bag is typically filled
|
|
102
|
+
* from outside the application at a request edge. Postgres refuses a NUL
|
|
103
|
+
* character (U+0000) and an unpaired surrogate in any string it stores, so an
|
|
104
|
+
* event whose bag holds one fails to append there while the in-memory and
|
|
105
|
+
* SQLite engines accept it; sanitise untrusted values before they reach the
|
|
106
|
+
* bag. And key ORDER is not preserved: Postgres stores an object's keys in an
|
|
107
|
+
* order of its own, so compare bags as unordered and never rely on the order
|
|
108
|
+
* `Object.keys` returns.
|
|
109
|
+
*
|
|
110
|
+
* Metadata is never part of a `Query` or an `AppendCondition` — a store refuses
|
|
111
|
+
* either when it carries a key the grammar does not define, so a metadata
|
|
112
|
+
* predicate cannot be written and silently ignored — never reaches a codec's
|
|
113
|
+
* `tagsFor`, and no store offers a way to rewrite it once appended. ADR-0017
|
|
114
|
+
* owns why each of those is mechanical rather than preferential.
|
|
115
|
+
*
|
|
116
|
+
* ## The one home of the shape rule
|
|
117
|
+
*
|
|
118
|
+
* The bag is a JSON OBJECT whose every value is a STRING, and this schema is
|
|
119
|
+
* where that rule is stated. At runtime it is checked in ONE place, at write: the
|
|
120
|
+
* codec's `toEvent` decodes every bag through this schema and refuses one that
|
|
121
|
+
* fails on its own error channel, so a value smuggled past the type (through an
|
|
122
|
+
* `any`) stops there. The schema judges a bag's OWN enumerable keys, so an object
|
|
123
|
+
* that keeps its entries anywhere else — a `Map`, a `Date` — reads as the empty
|
|
124
|
+
* bag rather than failing. A store checks no values: an event built by hand and handed
|
|
125
|
+
* to `append` directly carries whatever it carries, and what each engine then does
|
|
126
|
+
* with a non-string value differs — one may store it and read it back differently
|
|
127
|
+
* from another, and a `bigint` makes a SQL engine's append fail. Each
|
|
128
|
+
* SQL engine keeps a container check of its own on read — the column must hold a
|
|
129
|
+
* JSON object — because that defends against a writer outside the library and is
|
|
130
|
+
* about the column, not the values. A VALUE check must never be added on read
|
|
131
|
+
* alone: it would make an event the store accepted unreadable for ever, so a
|
|
132
|
+
* value check belongs at write or nowhere.
|
|
133
|
+
*/
|
|
134
|
+
export declare const Metadata: Schema.$Record<Schema.String, Schema.String>;
|
|
135
|
+
export type Metadata = typeof Metadata.Type;
|
|
136
|
+
/**
|
|
137
|
+
* An event as stored: an opaque payload plus its type, tags, a
|
|
138
|
+
* producer-supplied `uuid`, producer-supplied `occurredAt` domain time, and its
|
|
139
|
+
* `metadata` bag.
|
|
140
|
+
*
|
|
141
|
+
* `data` is `Uint8Array` (opaque bytes); the store does not interpret it.
|
|
142
|
+
* `uuid` is producer-supplied to keep `core` deterministic (no RNG in core).
|
|
143
|
+
* It is checked for UUID SHAPE only (`isGUID`), not for a version nibble: a
|
|
144
|
+
* stricter check would refuse ids an existing log already holds.
|
|
145
|
+
* `occurredAt` is informational only; ordering is by position alone.
|
|
146
|
+
* `metadata` mirrors `tags` rather than `data`: REQUIRED, with an empty bag
|
|
147
|
+
* legal. Required because an absent-versus-empty divergence between engines is
|
|
148
|
+
* invisible to `toEqual`, which ignores `undefined` properties, so an optional
|
|
149
|
+
* field could drift across the three engines with the round-trip suite green.
|
|
150
|
+
* Core only carries it; the codec's `toEvent` is where it is resolved.
|
|
151
|
+
*/
|
|
152
|
+
export declare const DcbEvent: Schema.Struct<{
|
|
153
|
+
readonly type: Schema.brand<Schema.String, "EventType">;
|
|
154
|
+
readonly data: Schema.Uint8Array;
|
|
155
|
+
readonly tags: Schema.$Array<Schema.brand<Schema.String, "Tag">>;
|
|
156
|
+
readonly uuid: Schema.String;
|
|
157
|
+
readonly occurredAt: Schema.DateTimeUtcFromString;
|
|
158
|
+
readonly metadata: Schema.$Record<Schema.String, Schema.String>;
|
|
159
|
+
}>;
|
|
160
|
+
export type DcbEvent = typeof DcbEvent.Type;
|
|
161
|
+
/** A persisted event: the event together with the position it was written at. */
|
|
162
|
+
export declare const SequencedEvent: Schema.Struct<{
|
|
163
|
+
readonly event: Schema.Struct<{
|
|
164
|
+
readonly type: Schema.brand<Schema.String, "EventType">;
|
|
165
|
+
readonly data: Schema.Uint8Array;
|
|
166
|
+
readonly tags: Schema.$Array<Schema.brand<Schema.String, "Tag">>;
|
|
167
|
+
readonly uuid: Schema.String;
|
|
168
|
+
readonly occurredAt: Schema.DateTimeUtcFromString;
|
|
169
|
+
readonly metadata: Schema.$Record<Schema.String, Schema.String>;
|
|
170
|
+
}>;
|
|
171
|
+
readonly position: Schema.brand<Schema.decodeTo<Schema.BigInt, Schema.String, never, never>, "Position">;
|
|
172
|
+
}>;
|
|
173
|
+
export type SequencedEvent = typeof SequencedEvent.Type;
|
|
174
|
+
//# sourceMappingURL=Event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Event.d.ts","sourceRoot":"","sources":["../../src/Event.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAIL,MAAM,EAGP,MAAM,QAAQ,CAAA;AAEf;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,QAAQ,uFAuBpB,CAAA;AACD,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C,8DAA8D;AAC9D,eAAO,MAAM,MAAM,EAAE,QAA4B,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,GAAG,oCAGf,CAAA;AACD,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAC,IAAI,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAA;CACtC;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,WAiBzC,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,uCAGrB,CAAA;AACD,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAE7C;;;GAGG;AACH,eAAO,MAAM,SAAS,0CAGrB,CAAA;AACD,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,QAAQ,8CAA8C,CAAA;AACnE,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ;;;;;;;EAOnB,CAAA;AACF,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C,iFAAiF;AACjF,eAAO,MAAM,cAAc;;;;;;;;;;EAGzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAA"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure projections and the composition primitive they fold through.
|
|
3
|
+
*
|
|
4
|
+
* A `Projection<S>` = `{ initialState, query, evolve }`: a query selecting the
|
|
5
|
+
* events it cares about and a pure `evolve` folding an already-*decoded* event
|
|
6
|
+
* into state. Decoding (wire bytes -> typed payload via `Schema`) is not core's
|
|
7
|
+
* job — it needs `Schema` and a `Serializer`, which core must not depend on —
|
|
8
|
+
* so `evolve` folds a `DecodedEvent` (the sequenced envelope plus an opaque
|
|
9
|
+
* `payload`). `composeProjections` merges several projections into one whose
|
|
10
|
+
* state is a named record.
|
|
11
|
+
*
|
|
12
|
+
* There is no builder and no decision-model orchestration here: both are the
|
|
13
|
+
* decode-aware `@kairos-es/codec` package's job (its `projection(...)` builder
|
|
14
|
+
* produces schema-backed projections, and its `modelAtHead` performs the effectful
|
|
15
|
+
* decode then folds through `composeProjections`, which `buildDecisionModel` builds
|
|
16
|
+
* on). Core keeps only the pure fold primitives.
|
|
17
|
+
*
|
|
18
|
+
* `Projection<S>` is *invariant* in `S`, so a heterogeneous record of
|
|
19
|
+
* projections (different state per key) cannot be bounded by
|
|
20
|
+
* `Projection<unknown>`. `AnyProjection` is the single authoritative,
|
|
21
|
+
* state-erased bound that owns that unsoundness in one place (built only via
|
|
22
|
+
* `AnyProjection.of`), so no downstream package writes `Projection<any>`; the
|
|
23
|
+
* precise per-key state is recovered by `StateOf` / `CompositeState`.
|
|
24
|
+
*
|
|
25
|
+
* The fold itself is pure and Effect-free.
|
|
26
|
+
*/
|
|
27
|
+
import type { SequencedEvent } from './Event.js';
|
|
28
|
+
import type { Query } from './Query.js';
|
|
29
|
+
/**
|
|
30
|
+
* An already-decoded event handed to a projection's fold: the sequenced wire
|
|
31
|
+
* envelope (position, type, tags, timings, metadata) plus the decoded `payload`.
|
|
32
|
+
*
|
|
33
|
+
* Core does not decode — it only folds events another layer has already decoded
|
|
34
|
+
* — so `payload` is `unknown` at this layer. `@kairos-es/codec`'s builder emits
|
|
35
|
+
* a projection whose `evolve` narrows `payload` per matched event type.
|
|
36
|
+
*/
|
|
37
|
+
export interface DecodedEvent {
|
|
38
|
+
readonly sequenced: SequencedEvent;
|
|
39
|
+
readonly payload: unknown;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A pure projection: the events it reads (`query`), its starting state, and a
|
|
43
|
+
* fold step.
|
|
44
|
+
*
|
|
45
|
+
* `evolve` contract: a projection is only ever invoked with an event that
|
|
46
|
+
* matches its `query`. `composeProjections` enforces this — it gates every
|
|
47
|
+
* sub-projection on its own query before dispatching — so an `evolve` may
|
|
48
|
+
* assume the event matched and need not re-check `matchesSequenced` itself. (It
|
|
49
|
+
* still dispatches by the event's type, which selects the fold; that is fold
|
|
50
|
+
* *selection*, not query gating.) `payload` has already been decoded by the
|
|
51
|
+
* codec layer.
|
|
52
|
+
*/
|
|
53
|
+
export interface Projection<S> {
|
|
54
|
+
readonly initialState: S;
|
|
55
|
+
readonly query: Query;
|
|
56
|
+
readonly evolve: (state: S, event: DecodedEvent) => S;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A state-erased view of a `Projection<S>`: the single authoritative bound for
|
|
60
|
+
* a *heterogeneous* record of projections. `Projection<S>` is invariant in `S`
|
|
61
|
+
* (S is both a parameter and the result of `evolve`), so `Record<string,
|
|
62
|
+
* Projection<unknown>>` rejects a record whose keys fold different state types.
|
|
63
|
+
* Rather than leak `Projection<any>` into every package that holds such a
|
|
64
|
+
* record, this type hides the invariant `evolve` behind an erased
|
|
65
|
+
* `(unknown) => unknown` surface and carries `S` only through the covariant
|
|
66
|
+
* `initialState`, so `Record<string, AnyProjection>` is sound with no `any`
|
|
67
|
+
* anywhere. Build one only via `AnyProjection.of`; recover the precise per-key
|
|
68
|
+
* `S` with `StateOf` / `CompositeState`. The `evolve` contract is the same as
|
|
69
|
+
* `Projection`'s: it is only ever called with an event matching `query`
|
|
70
|
+
* (`composeProjections` gates first), so an implementation may assume the match.
|
|
71
|
+
*/
|
|
72
|
+
export interface AnyProjection<out S = unknown> {
|
|
73
|
+
readonly initialState: S;
|
|
74
|
+
readonly query: Query;
|
|
75
|
+
readonly evolve: (state: unknown, event: DecodedEvent) => unknown;
|
|
76
|
+
}
|
|
77
|
+
export declare const AnyProjection: {
|
|
78
|
+
/**
|
|
79
|
+
* View a typed `Projection<S>` as an opaque `AnyProjection<S>`. This is the
|
|
80
|
+
* one place the invariant `evolve` is widened to the erased surface, so the
|
|
81
|
+
* unsoundness of a heterogeneous projection record is discharged here and
|
|
82
|
+
* nowhere else. `composeProjections` calls `evolve` with each projection's
|
|
83
|
+
* own state, so the widening is sound at runtime.
|
|
84
|
+
*/
|
|
85
|
+
readonly of: <S>(projection: Projection<S>) => AnyProjection<S>;
|
|
86
|
+
};
|
|
87
|
+
/** Recover the state type a projection folds to. */
|
|
88
|
+
export type StateOf<P> = P extends AnyProjection<infer S> ? S : never;
|
|
89
|
+
/**
|
|
90
|
+
* The record of named sub-states a composed projection (or decision model)
|
|
91
|
+
* folds to: each key's precise state type, recovered from its `AnyProjection`.
|
|
92
|
+
*/
|
|
93
|
+
export type CompositeState<T extends Record<string, AnyProjection>> = {
|
|
94
|
+
readonly [K in keyof T]: StateOf<T[K]>;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* OR-merge a list of queries into one. The empty list is the wildcard.
|
|
98
|
+
*
|
|
99
|
+
* The same merge `composeProjections` folds its record through, reachable
|
|
100
|
+
* WITHOUT a record — which is what a caller accumulating queries rather than
|
|
101
|
+
* projections needs, and the codec's read ledger is one. Without it that caller
|
|
102
|
+
* either hand-rolls the merge, or fabricates a projection per query to reach this
|
|
103
|
+
* one; both were tried and the first is a TRAP. An empty `items` is the WILDCARD
|
|
104
|
+
* and is absorbing under OR, while spreading an empty list contributes nothing, so
|
|
105
|
+
* a hand-rolled union silently narrows a match-everything query to its siblings —
|
|
106
|
+
* in the unsafe direction, and with nothing to notice.
|
|
107
|
+
*
|
|
108
|
+
* Seeding the fold with the wildcard would swallow everything for the same reason,
|
|
109
|
+
* so the list is reduced with no seed and the empty case answers directly.
|
|
110
|
+
*/
|
|
111
|
+
export declare const unionQueries: (queries: ReadonlyArray<Query>) => Query;
|
|
112
|
+
/**
|
|
113
|
+
* Compose named projections into one whose state is a record of sub-states and
|
|
114
|
+
* whose query is the OR-merge of all sub-queries. Each sub-projection only
|
|
115
|
+
* folds events matching its own query — the guarantee that `Projection.evolve`'s
|
|
116
|
+
* contract lets sub-projections rely on (so they need not re-gate themselves).
|
|
117
|
+
*
|
|
118
|
+
* The fold always writes each *matching* sub-result into a fresh record and
|
|
119
|
+
* returns it (no `changed` short-circuit, no `evolved !== prev` reference
|
|
120
|
+
* check): an in-place-mutating fold that returns the same reference would
|
|
121
|
+
* otherwise be silently dropped. At decision-model scale the extra copy is nil.
|
|
122
|
+
*/
|
|
123
|
+
export declare const composeProjections: <T extends Record<string, AnyProjection>>(projections: T) => Projection<CompositeState<T>>;
|
|
124
|
+
//# sourceMappingURL=Projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Projection.d.ts","sourceRoot":"","sources":["../../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAWpC;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,KAAK,CAAC,CAAA;CACtD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO;IAC5C,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAA;CAClE;AAED,eAAO,MAAM,aAAa;IACxB;;;;;;OAMG;kBACE,CAAC,cAAc,UAAU,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAAC;CAE5C,CAAA;AAEV,oDAAoD;AACpD,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AAErE;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,IAAI;IACpE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACvC,CAAA;AAqBD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,aAAa,CAAC,KAAK,CAAC,KAAG,KAGT,CAAA;AAEpD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACxE,aAAa,CAAC,KACb,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CA0B9B,CAAA"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DCB selection language: `Query` is an OR of `QueryItem`s. Within an item,
|
|
3
|
+
* `types` is an OR-filter (empty means any type) and `tags` is an AND superset
|
|
4
|
+
* test (the event must carry all of the item's tags; extra event tags are
|
|
5
|
+
* fine). The empty query (`items: []`) matches every event.
|
|
6
|
+
*
|
|
7
|
+
* The match predicate is a single pure module function over plain data;
|
|
8
|
+
* `Query` values carry no methods. See `matchesQuery` / `matchesQueryItem`.
|
|
9
|
+
*
|
|
10
|
+
* Smart constructors enforce the indexed fast-path restriction at the
|
|
11
|
+
* constructor surface (not via a runtime throw): every query built through them
|
|
12
|
+
* is the wildcard (`query.all`), a single type-only item (`query.types`), or an
|
|
13
|
+
* OR of tagged items each carrying at least one tag (`query.item` + `query.or`,
|
|
14
|
+
* or `query.tags` for the single-item case). The `query.item` constructor
|
|
15
|
+
* requires a non-empty `tags` at the type level, so there is no public way to
|
|
16
|
+
* build an unrestricted item (empty types and empty tags). `QueryItem`/`Query`
|
|
17
|
+
* are plain data, so internal decision-model plumbing can still assemble any
|
|
18
|
+
* shape directly; the restriction binds the public surface, where consumers
|
|
19
|
+
* build their consistency boundaries.
|
|
20
|
+
*
|
|
21
|
+
* The grammar's DISPATCH lives here too, beside the grammar it dispatches over:
|
|
22
|
+
* `ServableQueryShape` / `classifyServableQuery` own the three shapes an engine
|
|
23
|
+
* serves and the routing of the degenerate empty item.
|
|
24
|
+
*/
|
|
25
|
+
import { Array as Arr, Schema } from 'effect';
|
|
26
|
+
import { type DcbEvent, EventType, type SequencedEvent, Tag } from './Event.js';
|
|
27
|
+
/**
|
|
28
|
+
* One clause of a query: a set of types (OR, empty = any) AND a set of tags
|
|
29
|
+
* (superset test, empty = no tag constraint).
|
|
30
|
+
*/
|
|
31
|
+
export declare const QueryItem: Schema.Struct<{
|
|
32
|
+
readonly types: Schema.$Array<Schema.brand<Schema.String, "EventType">>;
|
|
33
|
+
readonly tags: Schema.$Array<Schema.brand<Schema.String, "Tag">>;
|
|
34
|
+
}>;
|
|
35
|
+
export type QueryItem = typeof QueryItem.Type;
|
|
36
|
+
/** A query: an OR of items. Empty `items` matches everything. */
|
|
37
|
+
export declare const Query: Schema.Struct<{
|
|
38
|
+
readonly items: Schema.$Array<Schema.Struct<{
|
|
39
|
+
readonly types: Schema.$Array<Schema.brand<Schema.String, "EventType">>;
|
|
40
|
+
readonly tags: Schema.$Array<Schema.brand<Schema.String, "Tag">>;
|
|
41
|
+
}>>;
|
|
42
|
+
}>;
|
|
43
|
+
export type Query = typeof Query.Type;
|
|
44
|
+
/**
|
|
45
|
+
* True when `event` satisfies `item`: its type is in `item.types` (or the item
|
|
46
|
+
* has no type constraint) AND the event's tags are a superset of `item.tags`.
|
|
47
|
+
*/
|
|
48
|
+
export declare const matchesQueryItem: (event: DcbEvent, item: QueryItem) => boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The one true match predicate (ported from `popo.py:48-54`): an empty query is
|
|
51
|
+
* a wildcard; otherwise the event must match at least one item (OR across
|
|
52
|
+
* items).
|
|
53
|
+
*/
|
|
54
|
+
export declare const matchesQuery: (event: DcbEvent, query: Query) => boolean;
|
|
55
|
+
/** Convenience predicate over a `SequencedEvent`. */
|
|
56
|
+
export declare const matchesSequenced: (sequenced: SequencedEvent, query: Query) => boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Assert `query` is *servable* by the indexed fast paths, throwing a plain
|
|
59
|
+
* `Error` describing the violation otherwise. This enforces at the store
|
|
60
|
+
* boundary the restriction the smart constructors enforce at the type level
|
|
61
|
+
* (`Query.ts` module docs) but which plain-data assembly — e.g. decision-model
|
|
62
|
+
* composition, `Projection.ts:31-36` — can still bypass.
|
|
63
|
+
*
|
|
64
|
+
* A query, or any of its items, carrying a key the grammar does not define is
|
|
65
|
+
* refused before anything else is judged (`unknownKeyFault` below says why).
|
|
66
|
+
*
|
|
67
|
+
* The servable grammar is exactly one of:
|
|
68
|
+
* - the wildcard (`items.length === 0`), which matches every event;
|
|
69
|
+
* - a *single* item (`items.length === 1`), any types/tags, including a
|
|
70
|
+
* type-only item; or
|
|
71
|
+
* - a *multi*-item query (`items.length > 1`) in which every item carries at
|
|
72
|
+
* least one tag.
|
|
73
|
+
*
|
|
74
|
+
* A multi-item query that mixes a tagless (type-only) item with tagged items is
|
|
75
|
+
* NOT servable: the by-type path and the by-tag path are indexed differently,
|
|
76
|
+
* so an OR spanning both cannot ride a single index. This is the previously
|
|
77
|
+
* un-enforced edge (`Projection.ts:31-36`).
|
|
78
|
+
*
|
|
79
|
+
* This function is pure (no clock, no RNG, no effect). Every engine applies it at
|
|
80
|
+
* its read/subscribe/append entry points, through the one shared
|
|
81
|
+
* `makeContractStore` decorator, so the rule is inherited rather than restated per
|
|
82
|
+
* backend; a throw inside a store `Effect` becomes a *defect* — a programming
|
|
83
|
+
* error, not an error-channel value — which is the intended "defect in every
|
|
84
|
+
* engine" behaviour.
|
|
85
|
+
*/
|
|
86
|
+
export declare const assertServableQuery: (query: Query) => void;
|
|
87
|
+
/**
|
|
88
|
+
* The unknown-key half of `servableQueryFault`, on its own: the refusal of a key the
|
|
89
|
+
* grammar does not define on the envelope or on any item, or `undefined`.
|
|
90
|
+
*
|
|
91
|
+
* It is the same rule and not a second statement of it — `servableQueryFault` calls
|
|
92
|
+
* this first. It is exported because ONE caller has to tell the two halves apart:
|
|
93
|
+
* the codec's read ledger refuses an unservable SHAPE on its own error channel, and
|
|
94
|
+
* must not report an unknown key as one. An unknown key is not something a slice's
|
|
95
|
+
* tags can fix, so the ledger leaves it to the store, which refuses it as a defect.
|
|
96
|
+
*/
|
|
97
|
+
export declare const unknownQueryKeyFault: (query: Query) => string | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* The servable grammar as a TOTAL function: the reason `query` cannot be served,
|
|
100
|
+
* or `undefined` when it can.
|
|
101
|
+
*
|
|
102
|
+
* The same rule as `assertServableQuery` above and NOT a second statement of it —
|
|
103
|
+
* that function is this one plus a throw, so the grammar is defined once and the
|
|
104
|
+
* two differ only in DISPOSITION. A store boundary wants the defect, because a
|
|
105
|
+
* non-servable query there is a programming error with no recovery; a caller that
|
|
106
|
+
* can still act on the answer wants a value, and the codec's read ledger is one:
|
|
107
|
+
* it asks whether a hop it is about to record would make the accumulated union
|
|
108
|
+
* unservable, and refuses on the error channel BEFORE the read is paid for.
|
|
109
|
+
*
|
|
110
|
+
* The alternative was for that caller to re-derive the rule, and it was tried:
|
|
111
|
+
* the codec's own copy tested each recorded query separately, which silently
|
|
112
|
+
* admitted a FIRST hop composed of two tagless slices — a multi-item query, and
|
|
113
|
+
* unservable, dying at the store one layer away. A grammar with two homes drifts
|
|
114
|
+
* at exactly the shapes neither author had in mind. The shape here is the read
|
|
115
|
+
* side's own (`projectionWiringFault` returns `string | undefined` for the same
|
|
116
|
+
* reason), so a fault is one exit route and one return type.
|
|
117
|
+
*/
|
|
118
|
+
export declare const servableQueryFault: (query: Query) => string | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* The three shapes a servable query is SERVED by, and therefore the only three an
|
|
121
|
+
* engine's plan compiler dispatches over: the wildcard, a single type-only item,
|
|
122
|
+
* and an all-tagged query. `assertServableQuery` has already refused the mixed
|
|
123
|
+
* tagless-plus-tagged multi-item query, so these three are exhaustive over
|
|
124
|
+
* everything that reaches a store.
|
|
125
|
+
*
|
|
126
|
+
* `typeOnly` carries a NON-EMPTY list of types, and that is the whole guard
|
|
127
|
+
* against the degenerate empty item: see `classifyServableQuery`.
|
|
128
|
+
*/
|
|
129
|
+
export type ServableQueryShape = {
|
|
130
|
+
readonly kind: 'wildcard';
|
|
131
|
+
} | {
|
|
132
|
+
readonly kind: 'typeOnly';
|
|
133
|
+
readonly types: Arr.NonEmptyReadonlyArray<EventType>;
|
|
134
|
+
} | {
|
|
135
|
+
readonly kind: 'tagged';
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Classify a servable `Query` into the shape that serves it.
|
|
139
|
+
*
|
|
140
|
+
* ## Why the dispatch lives beside the grammar
|
|
141
|
+
*
|
|
142
|
+
* The three shapes ARE the three bullets `assertServableQuery` above admits, so an
|
|
143
|
+
* engine that re-derives the dispatch is a second answer to "which shape is
|
|
144
|
+
* this?", and the shapes only stay in step while there is one answer. Core states
|
|
145
|
+
* the grammar, so stating the dispatch over it here is that one fact once rather
|
|
146
|
+
* than once per backend. It is pure — no clock, no RNG, no effect — and knows
|
|
147
|
+
* nothing of SQL: what a shape COMPILES to stays entirely the engine's.
|
|
148
|
+
*
|
|
149
|
+
* ## The degenerate empty item routes to the WILDCARD, structurally
|
|
150
|
+
*
|
|
151
|
+
* A single item with neither types nor tags — `{ items: [{ types: [], tags: [] }] }`
|
|
152
|
+
* — matches EVERY event: `matchesQueryItem` reads empty types as "any type" and
|
|
153
|
+
* empty tags as a vacuous superset test. It must therefore be served by the
|
|
154
|
+
* wildcard. Served by the type-only path it would compile to a value list with
|
|
155
|
+
* nothing in it, and a membership test against an empty list matches NOTHING — the
|
|
156
|
+
* exact inversion of what the query means. Nothing upstream stops it reaching an
|
|
157
|
+
* engine: `assertServableQuery` admits a SINGLE item of any shape, and the servable
|
|
158
|
+
* grammar forbids a tagless item only in a MULTI-item query, so this dispatch is
|
|
159
|
+
* the last place the divergence can be closed.
|
|
160
|
+
*
|
|
161
|
+
* It is closed by the SHAPE and not by a branch in front of it. `typeOnly` carries
|
|
162
|
+
* `NonEmptyReadonlyArray`, so there is no way to construct it from an empty list;
|
|
163
|
+
* the emptiness test IS the constructor, and deleting it does not silently restore
|
|
164
|
+
* the wrong routing, it stops compiling. A guard clause placed before the
|
|
165
|
+
* construction would have been deletable by anyone tidying what looks like a
|
|
166
|
+
* redundant check on a list that "obviously" has types in it.
|
|
167
|
+
*/
|
|
168
|
+
export declare const classifyServableQuery: (query: Query) => ServableQueryShape;
|
|
169
|
+
/** Namespaced smart constructors for `Query`. */
|
|
170
|
+
export declare const query: {
|
|
171
|
+
readonly all: () => Query;
|
|
172
|
+
readonly item: (fields: {
|
|
173
|
+
readonly tags: readonly [Tag, ...ReadonlyArray<Tag>];
|
|
174
|
+
readonly types?: ReadonlyArray<EventType>;
|
|
175
|
+
}) => QueryItem;
|
|
176
|
+
readonly or: (items: readonly [QueryItem, ...ReadonlyArray<QueryItem>]) => Query;
|
|
177
|
+
readonly tags: (tags: readonly [Tag, ...ReadonlyArray<Tag>], types?: ReadonlyArray<EventType>) => Query;
|
|
178
|
+
readonly types: (types: readonly [EventType, ...ReadonlyArray<EventType>]) => Query;
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=Query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../src/Query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAE5E;;;GAGG;AACH,eAAO,MAAM,SAAS;;;EAGpB,CAAA;AACF,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAE7C,iEAAiE;AACjE,eAAO,MAAM,KAAK;;;;;EAEhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAErC;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,QAAQ,EAAE,MAAM,SAAS,KAAG,OAOnE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,QAAQ,EAAE,OAAO,KAAK,KAAG,OAK5D,CAAA;AAED,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,GAC3B,WAAW,cAAc,EACzB,OAAO,KAAK,KACX,OAA+C,CAAA;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,KAAK,KAAG,IAKlD,CAAA;AAkDD;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,KAAK,KAAG,MAAM,GAAG,SAM1D,CAAA;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,KAAG,MAAM,GAAG,SAsB1D,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAA;CACrD,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,KAAK,KAAG,kBAWpD,CAAA;AAoDD,iDAAiD;AACjD,eAAO,MAAM,KAAK;wBAlCF,KAAK;4BATC;QACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAA;QACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAA;KAC1C,KAAG,SAAS;yBAkCJ,SAAS,CAAC,SAAS,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,KACvD,KAAK;0BAtBA,SAAS,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,UACnC,aAAa,CAAC,SAAS,CAAC,KAC/B,KAAK;4BAUC,SAAS,CAAC,SAAS,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,KACvD,KAAK;CAoBE,CAAA"}
|