@kairos-es/core 1.0.0-alpha.4 → 1.0.0-alpha.6
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 +2 -2
- 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 +29 -13
- package/dist/cjs/Event.js.map +1 -1
- package/dist/cjs/Query.js +1 -1
- package/dist/cjs/Query.js.map +1 -1
- package/dist/cjs/SubscribeMachine.js +158 -67
- package/dist/cjs/SubscribeMachine.js.map +1 -1
- package/dist/cjs/inMemory.js +4 -4
- 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 +3 -2
- 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 → dts-cjs}/Event.d.ts +31 -20
- package/dist/dts-cjs/Event.d.ts.map +1 -0
- package/dist/{dts → dts-cjs}/Query.d.ts +5 -5
- package/dist/dts-cjs/SubscribeMachine.d.ts +289 -0
- 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 +289 -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 +2 -2
- 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 +30 -14
- package/dist/esm/Event.js.map +1 -1
- package/dist/esm/Query.js +1 -1
- package/dist/esm/Query.js.map +1 -1
- package/dist/esm/SubscribeMachine.js +157 -67
- package/dist/esm/SubscribeMachine.js.map +1 -1
- package/dist/esm/inMemory.js +5 -5
- 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 +5 -4
- package/src/DcbEventStore.ts +3 -3
- package/src/Event.ts +49 -14
- package/src/Query.ts +1 -1
- package/src/SubscribeMachine.ts +278 -96
- package/src/inMemory.ts +6 -6
- 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.map +0 -1
- package/dist/dts/SubscribeMachine.d.ts +0 -179
- package/dist/dts/SubscribeMachine.d.ts.map +0 -1
- package/dist/dts/retryOnConflict.d.ts.map +0 -1
- /package/dist/{dts → dts-cjs}/Projection.d.ts +0 -0
- /package/dist/{dts → dts-cjs}/Projection.d.ts.map +0 -0
- /package/dist/{dts → dts-cjs}/Query.d.ts.map +0 -0
- /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
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* channel (`never` for the pure in-memory engine; `SqlError` for either SQL one).
|
|
9
9
|
* The decorator wraps those mechanics with the policy the contract demands — the
|
|
10
10
|
* policy the first two engines hand-duplicated, with the Postgres one even
|
|
11
|
-
* needing a bespoke `catchAll`/`instanceof
|
|
11
|
+
* needing a bespoke `catchAll`/`instanceof` (Effect 3's `catchAll`, which Effect 4
|
|
12
|
+
* names `catch`):
|
|
12
13
|
*
|
|
13
14
|
* - `assertServableQuery` at every verb's entry (read, the append condition's
|
|
14
15
|
* guard query, subscribe) — a non-servable query, or one carrying a key the
|
|
@@ -57,5 +58,5 @@ export interface RawDcbEventStore<E> {
|
|
|
57
58
|
* Wrap a raw engine's mechanics with the shared contract-enforcement policy,
|
|
58
59
|
* producing a full `DcbEventStore` service value.
|
|
59
60
|
*/
|
|
60
|
-
export declare const makeContractStore: <E>(raw: RawDcbEventStore<E>) => DcbEventStore["
|
|
61
|
+
export declare const makeContractStore: <E>(raw: RawDcbEventStore<E>) => DcbEventStore["Service"];
|
|
61
62
|
//# sourceMappingURL=ContractStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractStore.d.ts","sourceRoot":"","sources":["../../src/ContractStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,MAAM,EAAqB,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AACrD,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAA;AAKzD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,CACb,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,CACf,MAAM,EAAE,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAC3C,SAAS,CAAC,EAAE,eAAe,KACxB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,qBAAqB,GAAG,CAAC,CAAC,CAAA;IACvD,QAAQ,CAAC,SAAS,EAAE,CAClB,KAAK,EAAE,KAAK,EACZ,KAAK,CAAC,EAAE,QAAQ,KACb,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;CACnD;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EACjC,KAAK,gBAAgB,CAAC,CAAC,CAAC,KACvB,aAAa,CAAC,SAAS,CAoGxB,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `DcbEventStore` contract: a `Context.
|
|
2
|
+
* The `DcbEventStore` contract: a `Context.Service` with no bundled implementation.
|
|
3
3
|
* Consumers supply a `Layer` (the in-memory one lives in this package; each SQL
|
|
4
4
|
* engine is a package of its own — `@kairos-es/store-postgres` and
|
|
5
5
|
* `@kairos-es/store-sqlite`).
|
|
@@ -39,7 +39,7 @@ export interface ReadResponse {
|
|
|
39
39
|
readonly events: Stream.Stream<SequencedEvent>;
|
|
40
40
|
readonly head: Position;
|
|
41
41
|
}
|
|
42
|
-
declare const DcbEventStore_base: Context.
|
|
42
|
+
declare const DcbEventStore_base: Context.ServiceClass<DcbEventStore, "@kairos-es/core/DcbEventStore", {
|
|
43
43
|
/**
|
|
44
44
|
* Read events matching `query` (empty query = all), optionally bounded by
|
|
45
45
|
* `after`/`limit`. See head semantics in the module docs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DcbEventStore.d.ts","sourceRoot":"","sources":["../../src/DcbEventStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAChC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CACxB;;IAKG;;;OAGG;mBACY,CACb,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAEhC;;;;OAIG;qBACc,CACf,MAAM,EAAE,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAC3C,SAAS,CAAC,EAAE,eAAe,KACxB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAEnD;;;;OAIG;wBACiB,CAClB,KAAK,EAAE,KAAK,EACZ,KAAK,CAAC,EAAE,QAAQ,KACb,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;;AA9B1D,qBAAa,aAAc,SAAQ,
|
|
1
|
+
{"version":3,"file":"DcbEventStore.d.ts","sourceRoot":"","sources":["../../src/DcbEventStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAChC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CACxB;;IAKG;;;OAGG;mBACY,CACb,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;IAEhC;;;;OAIG;qBACc,CACf,MAAM,EAAE,GAAG,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAC3C,SAAS,CAAC,EAAE,eAAe,KACxB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAEnD;;;;OAIG;wBACiB,CAClB,KAAK,EAAE,KAAK,EACZ,KAAK,CAAC,EAAE,QAAQ,KACb,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;;AA9B1D,qBAAa,aAAc,SAAQ,kBAgCC;CAAG"}
|
|
@@ -12,11 +12,20 @@ import { Schema } from 'effect';
|
|
|
12
12
|
*
|
|
13
13
|
* The log is 64-bit (`bigserial` in Postgres) and exceeds JS `number` safety at
|
|
14
14
|
* 2^53, so positions are a `bigint`. They are compared, never arithmetic'd.
|
|
15
|
-
*
|
|
15
|
+
* Encoded, a position is a decimal string (JSON-safe, `String(b)`); decoded, a
|
|
16
16
|
* `bigint`. `0n` is the "before the first event" sentinel used by `after` and by
|
|
17
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.
|
|
18
27
|
*/
|
|
19
|
-
export declare const Position: Schema.brand<Schema.
|
|
28
|
+
export declare const Position: Schema.brand<Schema.decodeTo<Schema.BigInt, Schema.String, never, never>, "Position">;
|
|
20
29
|
export type Position = typeof Position.Type;
|
|
21
30
|
/** The sentinel position meaning "before the first event". */
|
|
22
31
|
export declare const ORIGIN: Position;
|
|
@@ -26,7 +35,7 @@ export declare const ORIGIN: Position;
|
|
|
26
35
|
* event sourcing; a query matches an event when the event carries all of an
|
|
27
36
|
* item's tags (superset test).
|
|
28
37
|
*/
|
|
29
|
-
export declare const Tag: Schema.brand<Schema.
|
|
38
|
+
export declare const Tag: Schema.brand<Schema.String, "Tag">;
|
|
30
39
|
export type Tag = typeof Tag.Type;
|
|
31
40
|
/**
|
|
32
41
|
* A reusable tag identity: a `key` plus a `.make(value)` that composes the
|
|
@@ -65,13 +74,13 @@ export declare const tagIdentity: (key: string) => TagIdentity;
|
|
|
65
74
|
* error channel (`read` keeps `E = never`). Constructed at call sites with
|
|
66
75
|
* `ReadLimit.make(n)`.
|
|
67
76
|
*/
|
|
68
|
-
export declare const ReadLimit: Schema.brand<Schema.
|
|
77
|
+
export declare const ReadLimit: Schema.brand<Schema.Int, "ReadLimit">;
|
|
69
78
|
export type ReadLimit = typeof ReadLimit.Type;
|
|
70
79
|
/**
|
|
71
80
|
* An event type name, a branded non-empty string. Versioning is by convention
|
|
72
81
|
* in the name (e.g. `course-registered.v2`); only non-emptiness is enforced.
|
|
73
82
|
*/
|
|
74
|
-
export declare const EventType: Schema.brand<Schema.
|
|
83
|
+
export declare const EventType: Schema.brand<Schema.String, "EventType">;
|
|
75
84
|
export type EventType = typeof EventType.Type;
|
|
76
85
|
/**
|
|
77
86
|
* An event's metadata: a flat bag of strings carrying the circumstances under
|
|
@@ -122,7 +131,7 @@ export type EventType = typeof EventType.Type;
|
|
|
122
131
|
* alone: it would make an event the store accepted unreadable for ever, so a
|
|
123
132
|
* value check belongs at write or nowhere.
|
|
124
133
|
*/
|
|
125
|
-
export declare const Metadata: Schema
|
|
134
|
+
export declare const Metadata: Schema.$Record<Schema.String, Schema.String>;
|
|
126
135
|
export type Metadata = typeof Metadata.Type;
|
|
127
136
|
/**
|
|
128
137
|
* An event as stored: an opaque payload plus its type, tags, a
|
|
@@ -131,6 +140,8 @@ export type Metadata = typeof Metadata.Type;
|
|
|
131
140
|
*
|
|
132
141
|
* `data` is `Uint8Array` (opaque bytes); the store does not interpret it.
|
|
133
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.
|
|
134
145
|
* `occurredAt` is informational only; ordering is by position alone.
|
|
135
146
|
* `metadata` mirrors `tags` rather than `data`: REQUIRED, with an empty bag
|
|
136
147
|
* legal. Required because an absent-versus-empty divergence between engines is
|
|
@@ -139,25 +150,25 @@ export type Metadata = typeof Metadata.Type;
|
|
|
139
150
|
* Core only carries it; the codec's `toEvent` is where it is resolved.
|
|
140
151
|
*/
|
|
141
152
|
export declare const DcbEvent: Schema.Struct<{
|
|
142
|
-
type: Schema.brand<Schema.
|
|
143
|
-
data:
|
|
144
|
-
tags: Schema
|
|
145
|
-
uuid:
|
|
146
|
-
occurredAt:
|
|
147
|
-
metadata: Schema
|
|
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>;
|
|
148
159
|
}>;
|
|
149
160
|
export type DcbEvent = typeof DcbEvent.Type;
|
|
150
161
|
/** A persisted event: the event together with the position it was written at. */
|
|
151
162
|
export declare const SequencedEvent: Schema.Struct<{
|
|
152
|
-
event: Schema.Struct<{
|
|
153
|
-
type: Schema.brand<Schema.
|
|
154
|
-
data:
|
|
155
|
-
tags: Schema
|
|
156
|
-
uuid:
|
|
157
|
-
occurredAt:
|
|
158
|
-
metadata: Schema
|
|
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>;
|
|
159
170
|
}>;
|
|
160
|
-
position: Schema.brand<Schema.
|
|
171
|
+
readonly position: Schema.brand<Schema.decodeTo<Schema.BigInt, Schema.String, never, never>, "Position">;
|
|
161
172
|
}>;
|
|
162
173
|
export type SequencedEvent = typeof SequencedEvent.Type;
|
|
163
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"}
|
|
@@ -29,15 +29,15 @@ import { type DcbEvent, EventType, type SequencedEvent, Tag } from './Event.js';
|
|
|
29
29
|
* (superset test, empty = no tag constraint).
|
|
30
30
|
*/
|
|
31
31
|
export declare const QueryItem: Schema.Struct<{
|
|
32
|
-
types: Schema
|
|
33
|
-
tags: Schema
|
|
32
|
+
readonly types: Schema.$Array<Schema.brand<Schema.String, "EventType">>;
|
|
33
|
+
readonly tags: Schema.$Array<Schema.brand<Schema.String, "Tag">>;
|
|
34
34
|
}>;
|
|
35
35
|
export type QueryItem = typeof QueryItem.Type;
|
|
36
36
|
/** A query: an OR of items. Empty `items` matches everything. */
|
|
37
37
|
export declare const Query: Schema.Struct<{
|
|
38
|
-
items: Schema
|
|
39
|
-
types: Schema
|
|
40
|
-
tags: Schema
|
|
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
41
|
}>>;
|
|
42
42
|
}>;
|
|
43
43
|
export type Query = typeof Query.Type;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The subscribe machine every engine's `subscribe` is built from — the in-memory
|
|
3
|
+
* store, `@kairos-es/store-postgres` and `@kairos-es/store-sqlite`: the wake queue,
|
|
4
|
+
* the wake-source drain, the paged catch-up reconcile, the `> boundary` de-dup (via
|
|
5
|
+
* the cursor's exclusive lower bound) and the poll loop that together turn a paged
|
|
6
|
+
* read into a live subscription.
|
|
7
|
+
*
|
|
8
|
+
* The algorithm is `subscribeItems`, generic over the item `A` it delivers and the
|
|
9
|
+
* cursor `C` it resumes from, because nothing in it is about an event: any source
|
|
10
|
+
* that can page-read strictly after a cursor and name the cursor of what it
|
|
11
|
+
* returned is subscribable by the same loop. `subscribeStream` is the event-store
|
|
12
|
+
* shape — `SequencedEvent` ordered by `Position` — over the same loop, and it is
|
|
13
|
+
* what every engine calls, unchanged by the generalisation. A second kind of
|
|
14
|
+
* source reuses the loop rather than growing a second one, for the reason the next
|
|
15
|
+
* paragraph gives.
|
|
16
|
+
*
|
|
17
|
+
* It lives in core because delivery semantics are contract semantics. An engine
|
|
18
|
+
* supplies TRANSPORT through two STUBBABLE seams — `fetch`, a paged read strictly
|
|
19
|
+
* after a cursor, and `listen`, an optional wake source — and inherits the delivery
|
|
20
|
+
* behaviour whole, exactly as it supplies mechanics to `makeContractStore` and
|
|
21
|
+
* inherits the contract policy whole. Two engines that each grew their own wake
|
|
22
|
+
* loop would be identical only by inspection, and `subscribe` is the verb where
|
|
23
|
+
* that is least affordable: a divergence there loses events rather than failing.
|
|
24
|
+
* Both seams take plain in-memory values in a test, so the wake/poll/de-dup logic
|
|
25
|
+
* is exercisable with no live backend at all.
|
|
26
|
+
*
|
|
27
|
+
* All THREE engines are callers, and the count matters to a reader here: it means
|
|
28
|
+
* there is exactly ONE delivery algorithm in this repo, and it is the code below. The
|
|
29
|
+
* in-memory store is a caller like the rest — it hands in a `fetch` over its log
|
|
30
|
+
* `Ref` and its append `PubSub` as a wake source and keeps no delivery logic of its
|
|
31
|
+
* own — which is what makes the shared contract suite's subscribe cases a comparison
|
|
32
|
+
* of two BACKENDS rather than of two delivery algorithms.
|
|
33
|
+
*
|
|
34
|
+
* Correctness rests on the periodic cursor-based reconcile — a re-read from the
|
|
35
|
+
* cursor every poll interval — and NEVER on the wake source, which only lowers
|
|
36
|
+
* delivery latency below that interval. A subscription with no wake source at all
|
|
37
|
+
* is therefore complete and correct, merely slower, which is why `listen` is
|
|
38
|
+
* optional in the TYPE rather than by the convention of passing `Stream.never`:
|
|
39
|
+
* poll-only delivery is a supported shape, not a degraded one.
|
|
40
|
+
*
|
|
41
|
+
* ## The precondition every caller inherits: ASCENDING COMMIT ORDER
|
|
42
|
+
*
|
|
43
|
+
* It binds EVERY caller, over whatever cursor it chose — an engine's `Position`, or
|
|
44
|
+
* any other `C` handed to `subscribeItems` — and the example below is written in
|
|
45
|
+
* positions only because the engines are the callers it was first written for. A
|
|
46
|
+
* cursor that is committed and VISIBLE must never be below one already delivered.
|
|
47
|
+
* Gaps are fine: the cursor is an ascending exclusive lower bound, so a number
|
|
48
|
+
* that is allocated and never committed, or never allocated at all, is simply a
|
|
49
|
+
* number the machine never sees. Out-of-order commits are not fine. If one
|
|
50
|
+
* writer allocates position 5 and another allocates 6, and 6 commits first, a poll
|
|
51
|
+
* landing between the two commits delivers 6 and advances the cursor past it — and
|
|
52
|
+
* 5 is then lost forever, because no later read looks below the cursor and nothing
|
|
53
|
+
* anywhere notices the hole. A caller whose concurrent writers can commit out of
|
|
54
|
+
* order MUST serialise them, or this machine will silently skip. An engine that
|
|
55
|
+
* takes an exclusive lock on both of its append paths discharges the obligation
|
|
56
|
+
* that way; an engine whose append is a single statement discharges it
|
|
57
|
+
* structurally.
|
|
58
|
+
*
|
|
59
|
+
* The obligation is written down HERE, in the algorithm that depends on it, rather
|
|
60
|
+
* than in whichever caller happens to satisfy it. It is a property of this delivery
|
|
61
|
+
* loop and of nothing else, so an author reading only their own source has
|
|
62
|
+
* no way to discover that anything downstream needs it — and the failure mode is
|
|
63
|
+
* the worst kind there is: no error, no defect, no failing test, just an event that
|
|
64
|
+
* a subscriber never receives. A single engine owning both its append path and its
|
|
65
|
+
* own wake loop can leave the assumption implicit; a shared machine cannot.
|
|
66
|
+
*
|
|
67
|
+
* ## `catchUpPageSize` bounds the READ, not the emission — unless `emit: 'page'`
|
|
68
|
+
*
|
|
69
|
+
* Under the default `emit: 'backlog'`, which is what `subscribeStream` and so every
|
|
70
|
+
* engine uses, `drainFrom` below PAGES the catch-up read — `catchUpPageSize` bounds
|
|
71
|
+
* each `fetch` — but it COLLECTS every page into one array, which is emitted as a
|
|
72
|
+
* SINGLE chunk. So peak memory at subscription start is O(backlog) rather than O(page): a
|
|
73
|
+
* subscriber joining behind a large log materialises the whole backlog before its
|
|
74
|
+
* consumer sees the first element.
|
|
75
|
+
*
|
|
76
|
+
* That is a memory characteristic and nothing more, which is why ONE chunk is
|
|
77
|
+
* enough here: downstream commit boundaries do not depend on the upstream chunking
|
|
78
|
+
* at all, because the read side's `Stream.groupedWithin` re-chunks the stream into
|
|
79
|
+
* bounded micro-batches whatever arrives (ADR-0007), so a projection's transactions
|
|
80
|
+
* stay small even against a one-chunk catch-up. The cost therefore lands on backlog
|
|
81
|
+
* memory alone, and it lands hardest on the durable-log-with-in-memory-view mode,
|
|
82
|
+
* where an ephemeral view re-catches-up the entire log on every boot.
|
|
83
|
+
*
|
|
84
|
+
* `emit: 'page'` changes exactly that and nothing else. Each fetched page is
|
|
85
|
+
* emitted as its own chunk and the cursor advances past it before the next read, so
|
|
86
|
+
* peak memory is O(page) and the consumer sees the first item after ONE read rather
|
|
87
|
+
* than after the whole backlog. Delivery is unchanged: the same items in the same
|
|
88
|
+
* order, the same de-dup, the same wake and poll. What a caller gives up is the
|
|
89
|
+
* one-chunk catch-up, so it must not read meaning into chunk boundaries under
|
|
90
|
+
* either mode. It is for a consumer that does NOT re-chunk downstream and whose
|
|
91
|
+
* catch-up can be the whole of a large source — a full resync of a table, say —
|
|
92
|
+
* where holding the backlog would be the cost the paging was meant to avoid. The
|
|
93
|
+
* engines keep `'backlog'` because the read side's re-chunking already bounds what
|
|
94
|
+
* they commit, and changing their emission would move a behaviour nobody asked to
|
|
95
|
+
* move.
|
|
96
|
+
*/
|
|
97
|
+
import { Effect, Schema, type Scope, Stream } from 'effect';
|
|
98
|
+
import type { Position, SequencedEvent } from './Event.js';
|
|
99
|
+
import { ReadLimit } from './Event.js';
|
|
100
|
+
/**
|
|
101
|
+
* A short catch-up page size default for the reconcile loop. Reads page by page
|
|
102
|
+
* until a short page signals the tail is drained, so a large backlog is streamed in
|
|
103
|
+
* bounded chunks rather than one unbounded read. Every engine takes its
|
|
104
|
+
* `catchUpPageSize` from here — each SQL engine as its config's default, the
|
|
105
|
+
* in-memory store as its options' — so backends cannot drift on tuning.
|
|
106
|
+
*/
|
|
107
|
+
export declare const DEFAULT_CATCH_UP_PAGE_SIZE = 512;
|
|
108
|
+
/**
|
|
109
|
+
* The default upper bound a subscriber waits for a wake before re-reading from its
|
|
110
|
+
* cursor ANYWAY (milliseconds). Every engine takes its `pollInterval` from here —
|
|
111
|
+
* each SQL engine as its config's default, the in-memory store as its options' — so
|
|
112
|
+
* backends cannot drift on tuning.
|
|
113
|
+
*
|
|
114
|
+
* This periodic position-based poll — not the wake source — IS the correctness
|
|
115
|
+
* guarantee: every committed position is reconciled within this interval whether or
|
|
116
|
+
* not a wake for it was ever heard. That covers a wake fired before the source was
|
|
117
|
+
* fully established, a coalesced or dropped wake, and a wake source that died
|
|
118
|
+
* silently and never said so. A wake source only lowers delivery latency BELOW this
|
|
119
|
+
* interval; it never carries delivery, so no engine's wake mechanism can make the
|
|
120
|
+
* right figure here engine-specific. It trades worst-case delivery latency against
|
|
121
|
+
* idle read volume and nothing else. Upstream `postgres_tt.py` picks the same
|
|
122
|
+
* one-second fallback poll (`conn.wait(..., interval=1)`).
|
|
123
|
+
*/
|
|
124
|
+
export declare const DEFAULT_POLL_INTERVAL_MILLIS = 1000;
|
|
125
|
+
/**
|
|
126
|
+
* The two subscribe-tuning config FIELDS, for an engine whose config is a `Schema`
|
|
127
|
+
* to spread into its own struct.
|
|
128
|
+
*
|
|
129
|
+
* These knobs are the machine's, not any engine's: they name what the loop above
|
|
130
|
+
* does, the figures they fall back to are the two constants declared beside them,
|
|
131
|
+
* and neither is derivable from a backend's own properties — which is why the
|
|
132
|
+
* defaults already live here. Shipping the FIELD rather than only the default is
|
|
133
|
+
* what closes the last gap in that arrangement. An engine that spreads these gets
|
|
134
|
+
* the key NAME, the VALIDATION and the DEFAULT from one place, so a caller who has
|
|
135
|
+
* tuned one engine's subscribe reads another's without translating, and the two
|
|
136
|
+
* cannot drift in what they ACCEPT any more than in what they assume. Two engines
|
|
137
|
+
* hand-authoring the identical `Schema.optionalKey(…)` twice were identical only
|
|
138
|
+
* by inspection.
|
|
139
|
+
*
|
|
140
|
+
* The bounds differ between the two because the quantities do. `pollInterval` is a
|
|
141
|
+
* duration, so a fraction of a millisecond is meaningful and only `isGreaterThan(0)`
|
|
142
|
+
* and `isFinite` matter: at zero or below the loop would spin without ever waiting for a
|
|
143
|
+
* wake, and at `Infinity` the periodic re-read — the half that carries delivery —
|
|
144
|
+
* would never come, which silently converts the wake source from an optimisation
|
|
145
|
+
* into the guarantee. `catchUpPageSize` is a COUNT, so it must be an integer
|
|
146
|
+
* (`Schema.Int` excludes `Infinity` with it): the reconcile loop compares a page's
|
|
147
|
+
* length against it to decide the tail is drained, and a page of zero would report
|
|
148
|
+
* "drained" forever without reading anything.
|
|
149
|
+
*
|
|
150
|
+
* `optionalKey` on both keeps the decoded type at `key?: T` rather than
|
|
151
|
+
* `T | undefined`, matching the repo's `exactOptionalPropertyTypes` discipline — an
|
|
152
|
+
* absent key, never an explicit `undefined`.
|
|
153
|
+
*/
|
|
154
|
+
export declare const subscriptionTuningFields: {
|
|
155
|
+
readonly pollInterval: Schema.optionalKey<Schema.Number>;
|
|
156
|
+
readonly catchUpPageSize: Schema.optionalKey<Schema.Int>;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* How a subscription hands its catch-up to the consumer: the whole backlog as ONE
|
|
160
|
+
* chunk (`'backlog'`), or each fetched page as a chunk of its own (`'page'`). The
|
|
161
|
+
* module doc's "`catchUpPageSize` bounds the READ" section owns what each costs.
|
|
162
|
+
*
|
|
163
|
+
* A closed union of two words rather than a boolean, so a call site says WHICH
|
|
164
|
+
* behaviour it chose instead of what it turned off, and a third shape, should one
|
|
165
|
+
* ever be argued for, is added beside these rather than encoded in a second flag.
|
|
166
|
+
*/
|
|
167
|
+
export type SubscriptionEmission = 'backlog' | 'page';
|
|
168
|
+
/**
|
|
169
|
+
* The machine's inputs over ANY item `A` with an ascending cursor `C`: the two seams,
|
|
170
|
+
* where the cursor comes from and starts, and the tuning.
|
|
171
|
+
*
|
|
172
|
+
* Generic in the item and the cursor because nothing in the algorithm is about an
|
|
173
|
+
* EVENT. The wake queue, the poll, the paged reconcile and the exclusive-lower-bound
|
|
174
|
+
* de-dup need only three things of what they deliver: a page-read strictly after a
|
|
175
|
+
* cursor, a way to read the cursor OFF a delivered item, and a starting cursor. A
|
|
176
|
+
* log of `SequencedEvent`s ordered by `Position` is one such source; a feed of rows
|
|
177
|
+
* ordered by a feed-local sequence is another, and a second copy of this loop for it
|
|
178
|
+
* would be exactly the divergence the module doc argues against.
|
|
179
|
+
*
|
|
180
|
+
* The machine never COMPARES two cursors. `fetch` is the only thing that interprets
|
|
181
|
+
* one — its exclusive lower bound is what does the de-dup — so `C` needs no ordering
|
|
182
|
+
* instance here, and the ascending-commit-order obligation in the module doc is the
|
|
183
|
+
* caller's, stated over whatever `C` it chose.
|
|
184
|
+
*
|
|
185
|
+
* `initial` is REQUIRED, unlike `subscribeStream`'s `after`. Only a cursor type the
|
|
186
|
+
* machine knows can have a default — `ORIGIN` for `Position` — and for any other the
|
|
187
|
+
* caller is the only party that knows what "before everything" is.
|
|
188
|
+
*
|
|
189
|
+
* `pollInterval` and `catchUpPageSize` are optional here and fall back to
|
|
190
|
+
* `DEFAULT_POLL_INTERVAL_MILLIS` and `DEFAULT_CATCH_UP_PAGE_SIZE`: a caller with no
|
|
191
|
+
* config of its own takes the same figures every engine does, from the same place.
|
|
192
|
+
* The event-store shape, `SubscriptionDeps`, keeps them required, because every
|
|
193
|
+
* engine already resolves them from its config and that type is unchanged.
|
|
194
|
+
*/
|
|
195
|
+
export interface ItemSubscriptionDeps<A, C, E> {
|
|
196
|
+
/**
|
|
197
|
+
* Page-read the items strictly after `after`, up to `limit`, ascending by cursor.
|
|
198
|
+
* Returning FEWER than `limit` signals the tail is drained. The exclusive lower
|
|
199
|
+
* bound IS the de-dup: a re-read from the cursor never returns what was delivered.
|
|
200
|
+
*/
|
|
201
|
+
readonly fetch: (after: C, limit: ReadLimit) => Effect.Effect<ReadonlyArray<A>, E>;
|
|
202
|
+
/** The cursor of a delivered item: the next `fetch` starts strictly after it. */
|
|
203
|
+
readonly cursorOf: (item: A) => C;
|
|
204
|
+
/** The cursor to start from, exclusive: the first `fetch` reads strictly after it. */
|
|
205
|
+
readonly initial: C;
|
|
206
|
+
/** The raw wake source — see `SubscriptionDeps.listen`, whose rules it shares. */
|
|
207
|
+
readonly listen?: Stream.Stream<unknown, E>;
|
|
208
|
+
/**
|
|
209
|
+
* The poll interval in milliseconds (default `DEFAULT_POLL_INTERVAL_MILLIS`):
|
|
210
|
+
* finite and greater than 0, or `subscribeItems` dies.
|
|
211
|
+
*/
|
|
212
|
+
readonly pollInterval?: number;
|
|
213
|
+
/**
|
|
214
|
+
* The catch-up page size (default `DEFAULT_CATCH_UP_PAGE_SIZE`): an integer
|
|
215
|
+
* greater than 0, or `subscribeItems` dies.
|
|
216
|
+
*/
|
|
217
|
+
readonly catchUpPageSize?: number;
|
|
218
|
+
/** The catch-up's chunking (default `'backlog'`); see `SubscriptionEmission`. */
|
|
219
|
+
readonly emit?: SubscriptionEmission;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The two seams the event-store subscribe machine runs over, plus the two tuning
|
|
223
|
+
* knobs: `ItemSubscriptionDeps` fixed to `SequencedEvent` and `Position`, the shape
|
|
224
|
+
* every engine's `subscribe` already hands to `subscribeStream`. `E` is the engine's
|
|
225
|
+
* own infrastructure error — whatever its reads fail with — and it stays on the
|
|
226
|
+
* channel through to `makeContractStore`, which dies it.
|
|
227
|
+
*
|
|
228
|
+
* ONE type parameter and not two, deliberately: a wake source with an error type of
|
|
229
|
+
* its own is `Stream.orDie`d by the caller, yielding a `Stream<unknown, never>`
|
|
230
|
+
* that is assignable here under Effect's covariant error channel. A second
|
|
231
|
+
* parameter would buy nothing and would have to be spelled at every call site. Nor
|
|
232
|
+
* is there an `R`: neither seam contributes a requirement, and unused generality is
|
|
233
|
+
* not free.
|
|
234
|
+
*/
|
|
235
|
+
export interface SubscriptionDeps<E> {
|
|
236
|
+
/**
|
|
237
|
+
* Page-read the events strictly after `from`, up to `limit`, ascending by
|
|
238
|
+
* position. Returning FEWER than `limit` signals the tail is drained.
|
|
239
|
+
*/
|
|
240
|
+
readonly fetch: (from: Position, limit: ReadLimit) => Effect.Effect<ReadonlyArray<SequencedEvent>, E>;
|
|
241
|
+
/**
|
|
242
|
+
* The raw wake source: each element is an OPAQUE wake signal. The machine taps it
|
|
243
|
+
* into its own wake queue and drains it on a background fibre; it never reads the
|
|
244
|
+
* elements, so anything an engine can push on a change will do.
|
|
245
|
+
*
|
|
246
|
+
* OPTIONAL, and omitting it defaults to `Stream.never` at the point of use — an
|
|
247
|
+
* engine with no wake mechanism gets poll-only delivery, which the poll loop
|
|
248
|
+
* already makes correct. Under `exactOptionalPropertyTypes` that means omitting
|
|
249
|
+
* the key entirely rather than passing `undefined`.
|
|
250
|
+
*/
|
|
251
|
+
readonly listen?: Stream.Stream<unknown, E>;
|
|
252
|
+
/** The poll interval in milliseconds (see `DEFAULT_POLL_INTERVAL_MILLIS`). */
|
|
253
|
+
readonly pollInterval: number;
|
|
254
|
+
/** The catch-up page size (see `DEFAULT_CATCH_UP_PAGE_SIZE`). */
|
|
255
|
+
readonly catchUpPageSize: number;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Build a subscription `Stream` over any ascending-cursor source: catch up from
|
|
259
|
+
* `initial`, then track the live tail. The cursor threads through the
|
|
260
|
+
* `Stream.paginate` state, starting at `initial` and advancing to the cursor of the
|
|
261
|
+
* last delivered item each batch; the stream never completes on its own (the next
|
|
262
|
+
* state is always `Option.some`), so the enclosing scope interrupts it on close. An
|
|
263
|
+
* `E` mid-stream stays on the channel, for the caller to die or to supervise.
|
|
264
|
+
*
|
|
265
|
+
* This is THE delivery algorithm, and `subscribeStream` runs the same machine. Under
|
|
266
|
+
* `emit: 'backlog'` a batch is everything a drain to the tail returned; under
|
|
267
|
+
* `'page'` it is one fetched page, so the paginate step and the page coincide and a
|
|
268
|
+
* full page is followed at once by the next read rather than by a wait.
|
|
269
|
+
*
|
|
270
|
+
* A `pollInterval` or `catchUpPageSize` the config fields would refuse is a DEFECT
|
|
271
|
+
* here, raised by the stream before it reads or forks anything, with a message
|
|
272
|
+
* naming the field. This caller may have no config whose decode would have caught
|
|
273
|
+
* it, and unchecked the two failures are silent or late: a page size of 0 gives a
|
|
274
|
+
* stream that never emits and never fails, and 1.5 dies only at the first read,
|
|
275
|
+
* inside `ReadLimit`, in words that name neither knob.
|
|
276
|
+
*/
|
|
277
|
+
export declare const subscribeItems: <A, C, E>(deps: ItemSubscriptionDeps<A, C, E>) => Stream.Stream<A, E, Scope.Scope>;
|
|
278
|
+
/**
|
|
279
|
+
* Build the event subscription `Stream`: catch up from `after`, then track the live
|
|
280
|
+
* tail. A THIN wrapper over the machine `subscribeItems` runs, fixing the item to
|
|
281
|
+
* `SequencedEvent`, the cursor to its `position`, the start to `ORIGIN` when `after`
|
|
282
|
+
* is absent, and the emission to the default `'backlog'` — so every engine's call
|
|
283
|
+
* site, and every figure it delivers, is what it was before the machine became
|
|
284
|
+
* generic. It skips `subscribeItems`' tuning check, for the reason on `machine`. An `E`
|
|
285
|
+
* mid-stream stays on the channel; `makeContractStore` dies it (subscribe is
|
|
286
|
+
* `E = never` on the contract).
|
|
287
|
+
*/
|
|
288
|
+
export declare const subscribeStream: <E>(deps: SubscriptionDeps<E>, after: Position | undefined) => Stream.Stream<SequencedEvent, E, Scope.Scope>;
|
|
289
|
+
//# sourceMappingURL=SubscribeMachine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubscribeMachine.d.ts","sourceRoot":"","sources":["../../src/SubscribeMachine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+FG;AACH,OAAO,EACL,MAAM,EAIN,MAAM,EACN,KAAK,KAAK,EACV,MAAM,EACP,MAAM,QAAQ,CAAA;AACf,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACvD,OAAO,EAAU,SAAS,EAAE,MAAM,SAAS,CAAA;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAA;AAWhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,wBAAwB;;;CAG3B,CAAA;AA2CV;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,MAAM,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,SAAS,KACb,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACvC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAA;IACjC,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC3C;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,iFAAiF;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,oBAAoB,CAAA;CACrC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,SAAS,KACb,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAA;IACpD;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC3C,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACpC,MAAM,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAClC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CAGjC,CAAA;AA4HD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,MAAM,gBAAgB,CAAC,CAAC,CAAC,EACzB,OAAO,QAAQ,GAAG,SAAS,KAC1B,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CAQ3C,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAY,MAAM,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAY,MAAM,EAAE,KAAK,EAAkC,MAAM,QAAQ,CAAA;AAIhF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAuL/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC3C,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAuID,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa,CAG5D,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,oBAAoB,KAC5B,KAAK,CAAC,KAAK,CAAC,aAAa,CAC+C,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @kairos-es/core: DCB-first event-sourcing contract and in-memory store for
|
|
3
|
-
* Effect
|
|
3
|
+
* Effect.
|
|
4
4
|
*
|
|
5
5
|
* Exposes the domain types (`DcbEvent`, `SequencedEvent`, `Position`, `Tag`,
|
|
6
6
|
* `EventType`), the selection language (`Query`, `QueryItem`, the pure match
|
|
@@ -35,12 +35,6 @@
|
|
|
35
35
|
* jitter draws from `Random.next`, a default service on the same footing as the
|
|
36
36
|
* clock rather than a host `Math.random`. No `node:*`, no wall-clock reading, no
|
|
37
37
|
* ungoverned RNG.
|
|
38
|
-
*
|
|
39
|
-
* Effect v3 caveat: the `Schedule` API below is the THREE-type-parameter
|
|
40
|
-
* `Schedule<Out, In, R>` that `effect@3` exports, with `Schedule.intersect`. The
|
|
41
|
-
* currently-published docs show a newer four-type-parameter `Schedule` without
|
|
42
|
-
* `intersect` — do not copy them; this file is written against the installed
|
|
43
|
-
* `effect@3` type definitions.
|
|
44
38
|
*/
|
|
45
39
|
import { Effect, Schedule } from 'effect';
|
|
46
40
|
import type { AppendConditionFailed } from './errors.js';
|
|
@@ -66,5 +60,5 @@ import type { AppendConditionFailed } from './errors.js';
|
|
|
66
60
|
* error when a consumer's graph loads both builds — a conflict would then escape
|
|
67
61
|
* unretried and silently.
|
|
68
62
|
*/
|
|
69
|
-
export declare const retryOnConflict: <A, E, R>(command: Effect.Effect<A, AppendConditionFailed | E, R>, schedule?: Schedule.Schedule<unknown, AppendConditionFailed | E, R>) => Effect.Effect<A, AppendConditionFailed | E, R>;
|
|
63
|
+
export declare const retryOnConflict: <A, E, R>(command: Effect.Effect<A, AppendConditionFailed | E, R>, schedule?: Schedule.Schedule<unknown, AppendConditionFailed | E, never, R>) => Effect.Effect<A, AppendConditionFailed | E, R>;
|
|
70
64
|
//# sourceMappingURL=retryOnConflict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retryOnConflict.d.ts","sourceRoot":"","sources":["../../src/retryOnConflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAE,MAAM,EAAa,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAqBrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACrC,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,EAAE,CAAC,CAAC,EACvD,WAAU,QAAQ,CAAC,QAAQ,CACzB,OAAO,EACP,qBAAqB,GAAG,CAAC,EACzB,KAAK,EACL,CAAC,CACgB,KAClB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,EAAE,CAAC,CAI5C,CAAA"}
|
|
@@ -20,7 +20,7 @@ import { DateTime } from 'effect';
|
|
|
20
20
|
*
|
|
21
21
|
* Ordering in the log is by `position` alone, so an event's `occurredAt` is
|
|
22
22
|
* informational; pinning it to the epoch keeps serialised bytes and comparisons
|
|
23
|
-
* stable across runs. Built via `DateTime.
|
|
23
|
+
* stable across runs. Built via `DateTime.makeUnsafe(0)` (the value is a
|
|
24
24
|
* compile-time constant, not read from the host clock).
|
|
25
25
|
*/
|
|
26
26
|
export declare const EPOCH: DateTime.Utc;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The optimistic-concurrency guard for `append`. The append fails if any event
|
|
3
|
+
* matching `failIfEventsMatch` exists strictly after `after`. This is the whole
|
|
4
|
+
* DCB consistency mechanism: "fail if anything I based my decision on has
|
|
5
|
+
* changed since I read it."
|
|
6
|
+
*
|
|
7
|
+
* `after` is exact-optional (absent, never `undefined`) per
|
|
8
|
+
* `exactOptionalPropertyTypes`. Absent means "scan the whole log".
|
|
9
|
+
*/
|
|
10
|
+
import { Schema } from 'effect';
|
|
11
|
+
export declare const AppendCondition: Schema.Struct<{
|
|
12
|
+
readonly failIfEventsMatch: Schema.Struct<{
|
|
13
|
+
readonly items: Schema.$Array<Schema.Struct<{
|
|
14
|
+
readonly types: Schema.$Array<Schema.brand<Schema.String, "EventType">>;
|
|
15
|
+
readonly tags: Schema.$Array<Schema.brand<Schema.String, "Tag">>;
|
|
16
|
+
}>>;
|
|
17
|
+
}>;
|
|
18
|
+
readonly after: Schema.optionalKey<Schema.brand<Schema.decodeTo<Schema.BigInt, Schema.String, never, never>, "Position">>;
|
|
19
|
+
}>;
|
|
20
|
+
export type AppendCondition = typeof AppendCondition.Type;
|
|
21
|
+
//# sourceMappingURL=AppendCondition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppendCondition.d.ts","sourceRoot":"","sources":["../../src/AppendCondition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B,eAAO,MAAM,eAAe;;;;;;;;EAG1B,CAAA;AACF,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAA"}
|