@kairos-es/core 0.0.0
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/LICENSE +28 -0
- package/README.md +33 -0
- package/dist/cjs/AppendCondition.js +26 -0
- package/dist/cjs/AppendCondition.js.map +1 -0
- package/dist/cjs/ContractStore.js +80 -0
- package/dist/cjs/ContractStore.js.map +1 -0
- package/dist/cjs/DcbEventStore.js +10 -0
- package/dist/cjs/DcbEventStore.js.map +1 -0
- package/dist/cjs/Event.js +89 -0
- package/dist/cjs/Event.js.map +1 -0
- package/dist/cjs/Projection.js +73 -0
- package/dist/cjs/Projection.js.map +1 -0
- package/dist/cjs/Query.js +221 -0
- package/dist/cjs/Query.js.map +1 -0
- package/dist/cjs/SubscribeMachine.js +235 -0
- package/dist/cjs/SubscribeMachine.js.map +1 -0
- package/dist/cjs/errors.js +22 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/inMemory.js +191 -0
- package/dist/cjs/inMemory.js.map +1 -0
- package/dist/cjs/index.js +124 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/retryOnConflict.js +95 -0
- package/dist/cjs/retryOnConflict.js.map +1 -0
- package/dist/cjs/testing.js +54 -0
- package/dist/cjs/testing.js.map +1 -0
- package/dist/dts/AppendCondition.d.ts +23 -0
- package/dist/dts/AppendCondition.d.ts.map +1 -0
- package/dist/dts/ContractStore.d.ts +56 -0
- package/dist/dts/ContractStore.d.ts.map +1 -0
- package/dist/dts/DcbEventStore.d.ts +64 -0
- package/dist/dts/DcbEventStore.d.ts.map +1 -0
- package/dist/dts/Event.d.ts +104 -0
- package/dist/dts/Event.d.ts.map +1 -0
- package/dist/dts/Projection.d.ts +108 -0
- package/dist/dts/Projection.d.ts.map +1 -0
- package/dist/dts/Query.d.ts +145 -0
- package/dist/dts/Query.d.ts.map +1 -0
- package/dist/dts/SubscribeMachine.d.ts +179 -0
- package/dist/dts/SubscribeMachine.d.ts.map +1 -0
- package/dist/dts/errors.d.ts +17 -0
- package/dist/dts/errors.d.ts.map +1 -0
- package/dist/dts/inMemory.d.ts +76 -0
- package/dist/dts/inMemory.d.ts.map +1 -0
- package/dist/dts/index.d.ts +35 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/retryOnConflict.d.ts +70 -0
- package/dist/dts/retryOnConflict.d.ts.map +1 -0
- package/dist/dts/testing.d.ts +39 -0
- package/dist/dts/testing.d.ts.map +1 -0
- package/dist/esm/AppendCondition.js +19 -0
- package/dist/esm/AppendCondition.js.map +1 -0
- package/dist/esm/ContractStore.js +73 -0
- package/dist/esm/ContractStore.js.map +1 -0
- package/dist/esm/DcbEventStore.js +3 -0
- package/dist/esm/DcbEventStore.js.map +1 -0
- package/dist/esm/Event.js +81 -0
- package/dist/esm/Event.js.map +1 -0
- package/dist/esm/Projection.js +66 -0
- package/dist/esm/Projection.js.map +1 -0
- package/dist/esm/Query.js +209 -0
- package/dist/esm/Query.js.map +1 -0
- package/dist/esm/SubscribeMachine.js +227 -0
- package/dist/esm/SubscribeMachine.js.map +1 -0
- package/dist/esm/errors.js +14 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/inMemory.js +184 -0
- package/dist/esm/inMemory.js.map +1 -0
- package/dist/esm/index.js +38 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/dist/esm/retryOnConflict.js +87 -0
- package/dist/esm/retryOnConflict.js.map +1 -0
- package/dist/esm/testing.js +46 -0
- package/dist/esm/testing.js.map +1 -0
- package/package.json +39 -0
- package/src/AppendCondition.ts +18 -0
- package/src/ContractStore.ts +148 -0
- package/src/DcbEventStore.ts +77 -0
- package/src/Event.ts +135 -0
- package/src/Projection.ts +168 -0
- package/src/Query.ts +237 -0
- package/src/SubscribeMachine.ts +316 -0
- package/src/errors.ts +19 -0
- package/src/inMemory.ts +356 -0
- package/src/index.ts +37 -0
- package/src/retryOnConflict.ts +100 -0
- package/src/testing.ts +47 -0
- package/testing/package.json +6 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Effect, Predicate, Schema, Stream } from 'effect';
|
|
2
|
+
import { ReadLimit } from "./Event.js";
|
|
3
|
+
import { assertServableQuery } from "./Query.js";
|
|
4
|
+
/** Decode an unknown limit through `ReadLimit`, dying on a defect-worthy value. */
|
|
5
|
+
const decodeReadLimit = /*#__PURE__*/Schema.decodeUnknownSync(ReadLimit);
|
|
6
|
+
/**
|
|
7
|
+
* Wrap a raw engine's mechanics with the shared contract-enforcement policy,
|
|
8
|
+
* producing a full `DcbEventStore` service value.
|
|
9
|
+
*/
|
|
10
|
+
export const makeContractStore = raw => ({
|
|
11
|
+
read: (query, options) => Effect.suspend(() => {
|
|
12
|
+
// Defects (surface when the Effect runs), keeping `read` at `E = never`.
|
|
13
|
+
assertServableQuery(query);
|
|
14
|
+
if (options?.limit !== undefined) {
|
|
15
|
+
decodeReadLimit(options.limit);
|
|
16
|
+
}
|
|
17
|
+
// Infrastructure faults are defects, not contract values.
|
|
18
|
+
return Effect.orDie(raw.read(query, options));
|
|
19
|
+
}),
|
|
20
|
+
append: (events, condition) => Effect.suspend(() => {
|
|
21
|
+
// An EMPTY batch is a plain-data caller bypassing the
|
|
22
|
+
// `NonEmptyReadonlyArray` type: there is nothing to append. The engines
|
|
23
|
+
// diverge on it at an unchecked boundary — Postgres inserts zero rows and
|
|
24
|
+
// its conditional function returns zero rows, indistinguishable from a
|
|
25
|
+
// conflict, so it surfaces a PHANTOM `AppendConditionFailed`, whereas
|
|
26
|
+
// in-memory silently returns the previous head (`start - 1n`). Make it a
|
|
27
|
+
// DEFECT here, at the one choke point, so the policy is definitionally
|
|
28
|
+
// identical across engines and zero-rows stays an unambiguous conflict
|
|
29
|
+
// signal (a throw inside the suspend becomes a defect, like
|
|
30
|
+
// `assertServableQuery`).
|
|
31
|
+
if (events.length === 0) {
|
|
32
|
+
throw new Error('makeContractStore.append: the events batch is empty; append requires ' + 'at least one event (a NonEmptyReadonlyArray). An empty batch is a ' + 'plain-data caller bypassing the type — a defect, not a conflict.');
|
|
33
|
+
}
|
|
34
|
+
// A non-servable guard query is a defect in every engine.
|
|
35
|
+
if (condition !== undefined) {
|
|
36
|
+
assertServableQuery(condition.failIfEventsMatch);
|
|
37
|
+
}
|
|
38
|
+
// Only `AppendConditionFailed` belongs on the contract error channel;
|
|
39
|
+
// every other fault (a `SqlError`, a `55P03` lock-timeout abort, …) is
|
|
40
|
+
// infrastructure and surfaces OFF the shared contract as a defect.
|
|
41
|
+
//
|
|
42
|
+
// Classified by `_tag`, NEVER by `instanceof`, and this is the site where
|
|
43
|
+
// that matters most: it is the ONE gate every backend's conflicts pass
|
|
44
|
+
// through, so a class-identity check here is a single point of silent
|
|
45
|
+
// failure for the whole write side. `@kairos-es/core` ships dual ESM/CJS,
|
|
46
|
+
// so a consumer's graph can hold two distinct `AppendConditionFailed`
|
|
47
|
+
// classes for one error — an engine wired against one copy of core and
|
|
48
|
+
// wrapped by a `makeContractStore` from another. Under `instanceof` that
|
|
49
|
+
// engine's GENUINE conflicts fail the class check and are `Effect.die`d
|
|
50
|
+
// as infrastructure, which does not merely mislabel them: it takes the
|
|
51
|
+
// conflict OFF the error channel, where `retryOnConflict` (which already
|
|
52
|
+
// matches on the tag, and correctly) can never see it to retry. The tag
|
|
53
|
+
// is the error's identity across realms; `retryOnConflict.ts` makes the
|
|
54
|
+
// argument at length and this is its other end.
|
|
55
|
+
//
|
|
56
|
+
// `Predicate.isTagged` is total over `unknown` — an object check, then
|
|
57
|
+
// `_tag` equality — so a `null`, a string or a raw `SqlError` on the
|
|
58
|
+
// engine's channel simply answers false and dies as it should. It also
|
|
59
|
+
// narrows the raw `AppendConditionFailed | E` to the contract's single
|
|
60
|
+
// error, which is what keeps `append` at `E = AppendConditionFailed`.
|
|
61
|
+
return Effect.catchAll(raw.append(events, condition), error => Predicate.isTagged('AppendConditionFailed')(error) ? Effect.fail(error) : Effect.die(error));
|
|
62
|
+
}),
|
|
63
|
+
subscribe: (query, after) =>
|
|
64
|
+
// `Effect.sync` runs when the stream is executed; a throw from
|
|
65
|
+
// `assertServableQuery` becomes a stream defect (identical rule to
|
|
66
|
+
// read/append). Infrastructure faults on the live tail die (subscribe is
|
|
67
|
+
// `E = never`).
|
|
68
|
+
Stream.unwrap(Effect.sync(() => {
|
|
69
|
+
assertServableQuery(query);
|
|
70
|
+
return Stream.orDie(raw.subscribe(query, after));
|
|
71
|
+
}))
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=ContractStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractStore.js","names":["Effect","Predicate","Schema","Stream","ReadLimit","assertServableQuery","decodeReadLimit","decodeUnknownSync","makeContractStore","raw","read","query","options","suspend","limit","undefined","orDie","append","events","condition","length","Error","failIfEventsMatch","catchAll","error","isTagged","fail","die","subscribe","after","unwrap","sync"],"sources":["../../src/ContractStore.ts"],"sourcesContent":[null],"mappings":"AAgCA,SAASA,MAAM,EAAEC,SAAS,EAAEC,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AAI1D,SAASC,SAAS,QAAQ,YAAS;AAEnC,SAASC,mBAAmB,QAAoB,YAAS;AAEzD;AACA,MAAMC,eAAe,gBAAGJ,MAAM,CAACK,iBAAiB,CAACH,SAAS,CAAC;AAwB3D;;;;AAIA,OAAO,MAAMI,iBAAiB,GAC5BC,GAAwB,KACG;EAC3BC,IAAI,EAAEA,CAACC,KAAK,EAAEC,OAAO,KACnBZ,MAAM,CAACa,OAAO,CAAC,MAAK;IAClB;IACAR,mBAAmB,CAACM,KAAK,CAAC;IAC1B,IAAIC,OAAO,EAAEE,KAAK,KAAKC,SAAS,EAAE;MAChCT,eAAe,CAACM,OAAO,CAACE,KAAK,CAAC;IAChC;IACA;IACA,OAAOd,MAAM,CAACgB,KAAK,CAACP,GAAG,CAACC,IAAI,CAACC,KAAK,EAAEC,OAAO,CAAC,CAAC;EAC/C,CAAC,CAAC;EAEJK,MAAM,EAAEA,CAACC,MAAM,EAAEC,SAAS,KACxBnB,MAAM,CAACa,OAAO,CAAC,MAAK;IAClB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIK,MAAM,CAACE,MAAM,KAAK,CAAC,EAAE;MACvB,MAAM,IAAIC,KAAK,CACb,uEAAuE,GACrE,oEAAoE,GACpE,kEAAkE,CACrE;IACH;IACA;IACA,IAAIF,SAAS,KAAKJ,SAAS,EAAE;MAC3BV,mBAAmB,CAACc,SAAS,CAACG,iBAAiB,CAAC;IAClD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,OAAOtB,MAAM,CAACuB,QAAQ,CAACd,GAAG,CAACQ,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,EAAGK,KAAK,IAC1DvB,SAAS,CAACwB,QAAQ,CAAC,uBAAuB,CAAC,CAACD,KAAK,CAAC,GAC9CxB,MAAM,CAAC0B,IAAI,CAACF,KAAK,CAAC,GAClBxB,MAAM,CAAC2B,GAAG,CAACH,KAAK,CAAC,CACtB;EACH,CAAC,CAAC;EAEJI,SAAS,EAAEA,CAACjB,KAAK,EAAEkB,KAAK;EACtB;EACA;EACA;EACA;EACA1B,MAAM,CAAC2B,MAAM,CACX9B,MAAM,CAAC+B,IAAI,CAAC,MAAK;IACf1B,mBAAmB,CAACM,KAAK,CAAC;IAC1B,OAAOR,MAAM,CAACa,KAAK,CAACP,GAAG,CAACmB,SAAS,CAACjB,KAAK,EAAEkB,KAAK,CAAC,CAAC;EAClD,CAAC,CAAC;CAEP,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DcbEventStore.js","names":["Context","DcbEventStore","Tag"],"sources":["../../src/DcbEventStore.ts"],"sourcesContent":[null],"mappings":"AAiBA,SAASA,OAAO,QAAQ,QAAQ;AA2BhC,OAAM,MAAOC,aAAc,sBAAQD,OAAO,CAACE,GAAG,CAAC,+BAA+B,CAAC,EAgC5E","ignoreList":[]}
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
* `Schema.BigInt` encodes to a decimal string (JSON-safe) and decodes to
|
|
16
|
+
* `bigint`. `0n` is the "before the first event" sentinel used by `after` and by
|
|
17
|
+
* an empty store's head.
|
|
18
|
+
*/
|
|
19
|
+
export const Position = /*#__PURE__*/Schema.BigInt.pipe(/*#__PURE__*/Schema.nonNegativeBigInt(), /*#__PURE__*/Schema.brand('Position'));
|
|
20
|
+
/** The sentinel position meaning "before the first event". */
|
|
21
|
+
export const ORIGIN = /*#__PURE__*/Position.make(0n);
|
|
22
|
+
/**
|
|
23
|
+
* A business identifier attached to an event, following the `key:value`
|
|
24
|
+
* convention (e.g. `student:s1`). Tags replace the per-stream id of classic
|
|
25
|
+
* event sourcing; a query matches an event when the event carries all of an
|
|
26
|
+
* item's tags (superset test).
|
|
27
|
+
*/
|
|
28
|
+
export const Tag = /*#__PURE__*/Schema.String.pipe(/*#__PURE__*/Schema.pattern(/^[^:]+:.+/, {
|
|
29
|
+
identifier: 'Tag'
|
|
30
|
+
}), /*#__PURE__*/Schema.brand('Tag'));
|
|
31
|
+
export const tagIdentity = key => {
|
|
32
|
+
if (key.length === 0 || key.includes(':')) {
|
|
33
|
+
throw new Error(`tagIdentity: key must be non-empty and contain no ':' (got ${JSON.stringify(key)})`);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
key,
|
|
37
|
+
make: value => {
|
|
38
|
+
if (value.length === 0 || value.includes(':')) {
|
|
39
|
+
throw new Error(`tagIdentity(${JSON.stringify(key)}).make: value must be non-empty and contain no ':' (got ${JSON.stringify(value)})`);
|
|
40
|
+
}
|
|
41
|
+
return Tag.make(`${key}:${value}`);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* A read cap: a branded non-negative integer bounding how many events a `read`
|
|
47
|
+
* returns. `0` returns none; an absent limit returns all matching events.
|
|
48
|
+
*
|
|
49
|
+
* The bound lives in the type, not merely in prose: a negative or non-integer
|
|
50
|
+
* limit is a programming error, so it is a DEFECT at the store boundary — the
|
|
51
|
+
* store validates through this schema and dies rather than surfacing it on the
|
|
52
|
+
* error channel (`read` keeps `E = never`). Constructed at call sites with
|
|
53
|
+
* `ReadLimit.make(n)`.
|
|
54
|
+
*/
|
|
55
|
+
export const ReadLimit = /*#__PURE__*/Schema.Int.pipe(/*#__PURE__*/Schema.greaterThanOrEqualTo(0), /*#__PURE__*/Schema.brand('ReadLimit'));
|
|
56
|
+
/**
|
|
57
|
+
* An event type name, a branded non-empty string. Versioning is by convention
|
|
58
|
+
* in the name (e.g. `course-registered.v2`); only non-emptiness is enforced.
|
|
59
|
+
*/
|
|
60
|
+
export const EventType = /*#__PURE__*/Schema.String.pipe(/*#__PURE__*/Schema.nonEmptyString(), /*#__PURE__*/Schema.brand('EventType'));
|
|
61
|
+
/**
|
|
62
|
+
* An event as stored: an opaque payload plus its type, tags, a
|
|
63
|
+
* producer-supplied `uuid`, and producer-supplied `occurredAt` domain time.
|
|
64
|
+
*
|
|
65
|
+
* `data` is `Uint8Array` (opaque bytes); the store does not interpret it.
|
|
66
|
+
* `uuid` is producer-supplied to keep `core` deterministic (no RNG in core).
|
|
67
|
+
* `occurredAt` is informational only; ordering is by position alone.
|
|
68
|
+
*/
|
|
69
|
+
export const DcbEvent = /*#__PURE__*/Schema.Struct({
|
|
70
|
+
type: EventType,
|
|
71
|
+
data: Schema.Uint8ArrayFromSelf,
|
|
72
|
+
tags: /*#__PURE__*/Schema.Array(Tag),
|
|
73
|
+
uuid: Schema.UUID,
|
|
74
|
+
occurredAt: Schema.DateTimeUtc
|
|
75
|
+
});
|
|
76
|
+
/** A persisted event: the event together with the position it was written at. */
|
|
77
|
+
export const SequencedEvent = /*#__PURE__*/Schema.Struct({
|
|
78
|
+
event: DcbEvent,
|
|
79
|
+
position: Position
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=Event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Event.js","names":["Schema","Position","BigInt","pipe","nonNegativeBigInt","brand","ORIGIN","make","Tag","String","pattern","identifier","tagIdentity","key","length","includes","Error","JSON","stringify","value","ReadLimit","Int","greaterThanOrEqualTo","EventType","nonEmptyString","DcbEvent","Struct","type","data","Uint8ArrayFromSelf","tags","Array","uuid","UUID","occurredAt","DateTimeUtc","SequencedEvent","event","position"],"sources":["../../src/Event.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;AAQA,SAASA,MAAM,QAAQ,QAAQ;AAE/B;;;;;;;;;AASA,OAAO,MAAMC,QAAQ,gBAAGD,MAAM,CAACE,MAAM,CAACC,IAAI,cACxCH,MAAM,CAACI,iBAAiB,EAAE,eAC1BJ,MAAM,CAACK,KAAK,CAAC,UAAU,CAAC,CACzB;AAGD;AACA,OAAO,MAAMC,MAAM,gBAAaL,QAAQ,CAACM,IAAI,CAAC,EAAE,CAAC;AAEjD;;;;;;AAMA,OAAO,MAAMC,GAAG,gBAAGR,MAAM,CAACS,MAAM,CAACN,IAAI,cACnCH,MAAM,CAACU,OAAO,CAAC,WAAW,EAAE;EAAEC,UAAU,EAAE;AAAK,CAAE,CAAC,eAClDX,MAAM,CAACK,KAAK,CAAC,KAAK,CAAC,CACpB;AA8BD,OAAO,MAAMO,WAAW,GAAIC,GAAW,IAAiB;EACtD,IAAIA,GAAG,CAACC,MAAM,KAAK,CAAC,IAAID,GAAG,CAACE,QAAQ,CAAC,GAAG,CAAC,EAAE;IACzC,MAAM,IAAIC,KAAK,CACb,8DAA8DC,IAAI,CAACC,SAAS,CAACL,GAAG,CAAC,GAAG,CACrF;EACH;EACA,OAAO;IACLA,GAAG;IACHN,IAAI,EAAGY,KAAK,IAAI;MACd,IAAIA,KAAK,CAACL,MAAM,KAAK,CAAC,IAAIK,KAAK,CAACJ,QAAQ,CAAC,GAAG,CAAC,EAAE;QAC7C,MAAM,IAAIC,KAAK,CACb,eAAeC,IAAI,CAACC,SAAS,CAACL,GAAG,CAAC,2DAA2DI,IAAI,CAACC,SAAS,CAACC,KAAK,CAAC,GAAG,CACtH;MACH;MACA,OAAOX,GAAG,CAACD,IAAI,CAAC,GAAGM,GAAG,IAAIM,KAAK,EAAE,CAAC;IACpC;GACD;AACH,CAAC;AAED;;;;;;;;;;AAUA,OAAO,MAAMC,SAAS,gBAAGpB,MAAM,CAACqB,GAAG,CAAClB,IAAI,cACtCH,MAAM,CAACsB,oBAAoB,CAAC,CAAC,CAAC,eAC9BtB,MAAM,CAACK,KAAK,CAAC,WAAW,CAAC,CAC1B;AAGD;;;;AAIA,OAAO,MAAMkB,SAAS,gBAAGvB,MAAM,CAACS,MAAM,CAACN,IAAI,cACzCH,MAAM,CAACwB,cAAc,EAAE,eACvBxB,MAAM,CAACK,KAAK,CAAC,WAAW,CAAC,CAC1B;AAGD;;;;;;;;AAQA,OAAO,MAAMoB,QAAQ,gBAAGzB,MAAM,CAAC0B,MAAM,CAAC;EACpCC,IAAI,EAAEJ,SAAS;EACfK,IAAI,EAAE5B,MAAM,CAAC6B,kBAAkB;EAC/BC,IAAI,eAAE9B,MAAM,CAAC+B,KAAK,CAACvB,GAAG,CAAC;EACvBwB,IAAI,EAAEhC,MAAM,CAACiC,IAAI;EACjBC,UAAU,EAAElC,MAAM,CAACmC;CACpB,CAAC;AAGF;AACA,OAAO,MAAMC,cAAc,gBAAGpC,MAAM,CAAC0B,MAAM,CAAC;EAC1CW,KAAK,EAAEZ,QAAQ;EACfa,QAAQ,EAAErC;CACX,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { matchesSequenced } from "./Query.js";
|
|
2
|
+
// The wildcard query, built directly (not via the public `query` surface).
|
|
3
|
+
// Decision-model composition assembles `Query` values from plain data because
|
|
4
|
+
// it may legitimately produce shapes the public fast-path surface withholds:
|
|
5
|
+
// composing a type-only projection with tagged ones yields an OR that mixes a
|
|
6
|
+
// tagless item with tagged items. That is the one restriction edge left
|
|
7
|
+
// un-enforced by construction, deliberately, in this internal layer.
|
|
8
|
+
const WILDCARD = {
|
|
9
|
+
items: []
|
|
10
|
+
};
|
|
11
|
+
export const AnyProjection = {
|
|
12
|
+
/**
|
|
13
|
+
* View a typed `Projection<S>` as an opaque `AnyProjection<S>`. This is the
|
|
14
|
+
* one place the invariant `evolve` is widened to the erased surface, so the
|
|
15
|
+
* unsoundness of a heterogeneous projection record is discharged here and
|
|
16
|
+
* nowhere else. `composeProjections` calls `evolve` with each projection's
|
|
17
|
+
* own state, so the widening is sound at runtime.
|
|
18
|
+
*/
|
|
19
|
+
of: projection => projection
|
|
20
|
+
};
|
|
21
|
+
/** Merge two queries by OR-ing their items; either wildcard yields a wildcard. */
|
|
22
|
+
const mergeQuery = (left, right) => {
|
|
23
|
+
if (left.items.length === 0 || right.items.length === 0) {
|
|
24
|
+
return WILDCARD;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
items: [...left.items, ...right.items]
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Compose named projections into one whose state is a record of sub-states and
|
|
32
|
+
* whose query is the OR-merge of all sub-queries. Each sub-projection only
|
|
33
|
+
* folds events matching its own query — the guarantee that `Projection.evolve`'s
|
|
34
|
+
* contract lets sub-projections rely on (so they need not re-gate themselves).
|
|
35
|
+
*
|
|
36
|
+
* The fold always writes each *matching* sub-result into a fresh record and
|
|
37
|
+
* returns it (no `changed` short-circuit, no `evolved !== prev` reference
|
|
38
|
+
* check): an in-place-mutating fold that returns the same reference would
|
|
39
|
+
* otherwise be silently dropped. At decision-model scale the extra copy is nil.
|
|
40
|
+
*/
|
|
41
|
+
export const composeProjections = projections => {
|
|
42
|
+
const keys = Object.keys(projections);
|
|
43
|
+
const initialState = Object.fromEntries(keys.map(key => [key, projections[key].initialState]));
|
|
44
|
+
// Seeding the reduce with the wildcard would swallow everything (mergeQuery
|
|
45
|
+
// treats an empty-items query as a wildcard), so fold the sub-queries
|
|
46
|
+
// directly with no seed.
|
|
47
|
+
const mergedQuery = keys.length === 0 ? WILDCARD : keys.map(key => projections[key].query).reduce((acc, q) => mergeQuery(acc, q));
|
|
48
|
+
return {
|
|
49
|
+
initialState,
|
|
50
|
+
query: mergedQuery,
|
|
51
|
+
evolve: (state, decoded) => {
|
|
52
|
+
const next = {
|
|
53
|
+
...state
|
|
54
|
+
};
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
const projection = projections[key];
|
|
57
|
+
if (!matchesSequenced(decoded.sequenced, projection.query)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
next[key] = projection.evolve(state[key], decoded);
|
|
61
|
+
}
|
|
62
|
+
return next;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=Projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Projection.js","names":["matchesSequenced","WILDCARD","items","AnyProjection","of","projection","mergeQuery","left","right","length","composeProjections","projections","keys","Object","initialState","fromEntries","map","key","mergedQuery","query","reduce","acc","q","evolve","state","decoded","next","sequenced"],"sources":["../../src/Projection.ts"],"sourcesContent":[null],"mappings":"AA4BA,SAASA,gBAAgB,QAAQ,YAAS;AAE1C;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,QAAQ,GAAU;EAAEC,KAAK,EAAE;AAAE,CAAE;AAqDrC,OAAO,MAAMC,aAAa,GAAG;EAC3B;;;;;;;EAOAC,EAAE,EAAMC,UAAyB,IAC/BA;CACM;AAaV;AACA,MAAMC,UAAU,GAAGA,CAACC,IAAW,EAAEC,KAAY,KAAW;EACtD,IAAID,IAAI,CAACL,KAAK,CAACO,MAAM,KAAK,CAAC,IAAID,KAAK,CAACN,KAAK,CAACO,MAAM,KAAK,CAAC,EAAE;IACvD,OAAOR,QAAQ;EACjB;EACA,OAAO;IAAEC,KAAK,EAAE,CAAC,GAAGK,IAAI,CAACL,KAAK,EAAE,GAAGM,KAAK,CAACN,KAAK;EAAC,CAAE;AACnD,CAAC;AAED;;;;;;;;;;;AAWA,OAAO,MAAMQ,kBAAkB,GAC7BC,WAAc,IACmB;EACjC,MAAMC,IAAI,GAAGC,MAAM,CAACD,IAAI,CAACD,WAAW,CAAoC;EAExE,MAAMG,YAAY,GAAGD,MAAM,CAACE,WAAW,CACrCH,IAAI,CAACI,GAAG,CAAEC,GAAG,IAAK,CAACA,GAAG,EAAEN,WAAW,CAACM,GAAG,CAAC,CAACH,YAAY,CAAC,CAAC,CACnC;EAEtB;EACA;EACA;EACA,MAAMI,WAAW,GACfN,IAAI,CAACH,MAAM,KAAK,CAAC,GACbR,QAAQ,GACRW,IAAI,CACDI,GAAG,CAAEC,GAAG,IAAKN,WAAW,CAACM,GAAG,CAAC,CAACE,KAAK,CAAC,CACpCC,MAAM,CAAC,CAACC,GAAG,EAAEC,CAAC,KAAKhB,UAAU,CAACe,GAAG,EAAEC,CAAC,CAAC,CAAC;EAE/C,OAAO;IACLR,YAAY;IACZK,KAAK,EAAED,WAAW;IAClBK,MAAM,EAAEA,CAACC,KAAK,EAAEC,OAAO,KAAI;MACzB,MAAMC,IAAI,GAAG;QAAE,GAAGF;MAAK,CAAuC;MAC9D,KAAK,MAAMP,GAAG,IAAIL,IAAI,EAAE;QACtB,MAAMP,UAAU,GAAGM,WAAW,CAACM,GAAG,CAAC;QACnC,IAAI,CAACjB,gBAAgB,CAACyB,OAAO,CAACE,SAAS,EAAEtB,UAAU,CAACc,KAAK,CAAC,EAAE;UAC1D;QACF;QACAO,IAAI,CAACT,GAAG,CAAC,GAAGZ,UAAU,CAACkB,MAAM,CAACC,KAAK,CAACP,GAAG,CAAC,EAAEQ,OAAO,CAEhD;MACH;MACA,OAAOC,IAAI;IACb;GACD;AACH,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,209 @@
|
|
|
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 { EventType, 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 const QueryItem = /*#__PURE__*/Schema.Struct({
|
|
32
|
+
types: /*#__PURE__*/Schema.Array(EventType),
|
|
33
|
+
tags: /*#__PURE__*/Schema.Array(Tag)
|
|
34
|
+
});
|
|
35
|
+
/** A query: an OR of items. Empty `items` matches everything. */
|
|
36
|
+
export const Query = /*#__PURE__*/Schema.Struct({
|
|
37
|
+
items: /*#__PURE__*/Schema.Array(QueryItem)
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* True when `event` satisfies `item`: its type is in `item.types` (or the item
|
|
41
|
+
* has no type constraint) AND the event's tags are a superset of `item.tags`.
|
|
42
|
+
*/
|
|
43
|
+
export const matchesQueryItem = (event, item) => {
|
|
44
|
+
const typeOk = item.types.length === 0 || item.types.includes(event.type);
|
|
45
|
+
if (!typeOk) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const eventTags = new Set(event.tags);
|
|
49
|
+
return item.tags.every(tag => eventTags.has(tag));
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The one true match predicate (ported from `popo.py:48-54`): an empty query is
|
|
53
|
+
* a wildcard; otherwise the event must match at least one item (OR across
|
|
54
|
+
* items).
|
|
55
|
+
*/
|
|
56
|
+
export const matchesQuery = (event, query) => {
|
|
57
|
+
if (query.items.length === 0) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return query.items.some(item => matchesQueryItem(event, item));
|
|
61
|
+
};
|
|
62
|
+
/** Convenience predicate over a `SequencedEvent`. */
|
|
63
|
+
export const matchesSequenced = (sequenced, query) => matchesQuery(sequenced.event, query);
|
|
64
|
+
/**
|
|
65
|
+
* Assert `query` is *servable* by the indexed fast paths, throwing a plain
|
|
66
|
+
* `Error` describing the violation otherwise. This enforces at the store
|
|
67
|
+
* boundary the restriction the smart constructors enforce at the type level
|
|
68
|
+
* (`Query.ts` module docs) but which plain-data assembly — e.g. decision-model
|
|
69
|
+
* composition, `Projection.ts:31-36` — can still bypass.
|
|
70
|
+
*
|
|
71
|
+
* The servable grammar is exactly one of:
|
|
72
|
+
* - the wildcard (`items.length === 0`), which matches every event;
|
|
73
|
+
* - a *single* item (`items.length === 1`), any types/tags, including a
|
|
74
|
+
* type-only item; or
|
|
75
|
+
* - a *multi*-item query (`items.length > 1`) in which every item carries at
|
|
76
|
+
* least one tag.
|
|
77
|
+
*
|
|
78
|
+
* A multi-item query that mixes a tagless (type-only) item with tagged items is
|
|
79
|
+
* NOT servable: the by-type path and the by-tag path are indexed differently,
|
|
80
|
+
* so an OR spanning both cannot ride a single index. This is the previously
|
|
81
|
+
* un-enforced edge (`Projection.ts:31-36`).
|
|
82
|
+
*
|
|
83
|
+
* This function is pure (no clock, no RNG, no effect). Every engine applies it at
|
|
84
|
+
* its read/subscribe/append entry points, through the one shared
|
|
85
|
+
* `makeContractStore` decorator, so the rule is inherited rather than restated per
|
|
86
|
+
* backend; a throw inside a store `Effect` becomes a *defect* — a programming
|
|
87
|
+
* error, not an error-channel value — which is the intended "defect in every
|
|
88
|
+
* engine" behaviour.
|
|
89
|
+
*/
|
|
90
|
+
export const assertServableQuery = query => {
|
|
91
|
+
const {
|
|
92
|
+
items
|
|
93
|
+
} = query;
|
|
94
|
+
// Wildcard and single-item queries are always servable.
|
|
95
|
+
if (items.length <= 1) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// A multi-item query is servable only if every item is tagged; a tagless
|
|
99
|
+
// (type-only) item mixed with tagged items cannot ride a single index.
|
|
100
|
+
const hasTaglessItem = items.some(item => item.tags.length === 0);
|
|
101
|
+
if (hasTaglessItem) {
|
|
102
|
+
throw new Error('assertServableQuery: a multi-item query must have every item carry at ' + 'least one tag; a type-only item mixed with tagged items is not servable');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Classify a servable `Query` into the shape that serves it.
|
|
107
|
+
*
|
|
108
|
+
* ## Why the dispatch lives beside the grammar
|
|
109
|
+
*
|
|
110
|
+
* The three shapes ARE the three bullets `assertServableQuery` above admits, so an
|
|
111
|
+
* engine that re-derives the dispatch is a second answer to "which shape is
|
|
112
|
+
* this?", and the shapes only stay in step while there is one answer. Core states
|
|
113
|
+
* the grammar, so stating the dispatch over it here is that one fact once rather
|
|
114
|
+
* than once per backend. It is pure — no clock, no RNG, no effect — and knows
|
|
115
|
+
* nothing of SQL: what a shape COMPILES to stays entirely the engine's.
|
|
116
|
+
*
|
|
117
|
+
* ## The degenerate empty item routes to the WILDCARD, structurally
|
|
118
|
+
*
|
|
119
|
+
* A single item with neither types nor tags — `{ items: [{ types: [], tags: [] }] }`
|
|
120
|
+
* — matches EVERY event: `matchesQueryItem` reads empty types as "any type" and
|
|
121
|
+
* empty tags as a vacuous superset test. It must therefore be served by the
|
|
122
|
+
* wildcard. Served by the type-only path it would compile to a value list with
|
|
123
|
+
* nothing in it, and a membership test against an empty list matches NOTHING — the
|
|
124
|
+
* exact inversion of what the query means. Nothing upstream stops it reaching an
|
|
125
|
+
* engine: `assertServableQuery` admits a SINGLE item of any shape, and the servable
|
|
126
|
+
* grammar forbids a tagless item only in a MULTI-item query, so this dispatch is
|
|
127
|
+
* the last place the divergence can be closed.
|
|
128
|
+
*
|
|
129
|
+
* It is closed by the SHAPE and not by a branch in front of it. `typeOnly` carries
|
|
130
|
+
* `NonEmptyReadonlyArray`, so there is no way to construct it from an empty list;
|
|
131
|
+
* the emptiness test IS the constructor, and deleting it does not silently restore
|
|
132
|
+
* the wrong routing, it stops compiling. A guard clause placed before the
|
|
133
|
+
* construction would have been deletable by anyone tidying what looks like a
|
|
134
|
+
* redundant check on a list that "obviously" has types in it.
|
|
135
|
+
*/
|
|
136
|
+
export const classifyServableQuery = query => {
|
|
137
|
+
if (query.items.length === 0) {
|
|
138
|
+
return {
|
|
139
|
+
kind: 'wildcard'
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const only = query.items.length === 1 ? query.items[0] : undefined;
|
|
143
|
+
if (only !== undefined && only.tags.length === 0) {
|
|
144
|
+
return Arr.isNonEmptyReadonlyArray(only.types) ? {
|
|
145
|
+
kind: 'typeOnly',
|
|
146
|
+
types: only.types
|
|
147
|
+
} : {
|
|
148
|
+
kind: 'wildcard'
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
kind: 'tagged'
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
// --- Smart constructors (the fast-path-enforcing surface) -------------------
|
|
156
|
+
/**
|
|
157
|
+
* A query item carrying at least one tag (superset test), optionally narrowed
|
|
158
|
+
* to `types`. The non-empty `tags` requirement is the fast-path restriction,
|
|
159
|
+
* enforced at the type level rather than as a runtime throw. Combine several
|
|
160
|
+
* items with `or`.
|
|
161
|
+
*/
|
|
162
|
+
const item = fields => ({
|
|
163
|
+
types: fields.types ?? [],
|
|
164
|
+
tags: fields.tags
|
|
165
|
+
});
|
|
166
|
+
/** The wildcard query: matches every event. */
|
|
167
|
+
const all = () => ({
|
|
168
|
+
items: []
|
|
169
|
+
});
|
|
170
|
+
/**
|
|
171
|
+
* A single-item query matching events that carry all of `tags`, optionally also
|
|
172
|
+
* constrained to `types`. The common indexed fast-path shape.
|
|
173
|
+
*/
|
|
174
|
+
const tags = (tags, types) => ({
|
|
175
|
+
items: [item({
|
|
176
|
+
tags,
|
|
177
|
+
...(types === undefined ? {} : {
|
|
178
|
+
types
|
|
179
|
+
})
|
|
180
|
+
})]
|
|
181
|
+
});
|
|
182
|
+
/**
|
|
183
|
+
* A single type-only query (the other indexed fast path): one item constrained
|
|
184
|
+
* to `types` with no tag filter. This is the only tagless item shape the
|
|
185
|
+
* surface admits, and only as the whole query.
|
|
186
|
+
*/
|
|
187
|
+
const types = types => ({
|
|
188
|
+
items: [{
|
|
189
|
+
types,
|
|
190
|
+
tags: []
|
|
191
|
+
}]
|
|
192
|
+
});
|
|
193
|
+
/**
|
|
194
|
+
* An OR of query items. Each item should be built with `item` (which requires
|
|
195
|
+
* at least one tag); combining bare type-only items is intentionally not
|
|
196
|
+
* offered (use `types` for the single type-only query).
|
|
197
|
+
*/
|
|
198
|
+
const or = items => ({
|
|
199
|
+
items
|
|
200
|
+
});
|
|
201
|
+
/** Namespaced smart constructors for `Query`. */
|
|
202
|
+
export const query = {
|
|
203
|
+
all,
|
|
204
|
+
item,
|
|
205
|
+
or,
|
|
206
|
+
tags,
|
|
207
|
+
types
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=Query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Query.js","names":["Array","Arr","Schema","EventType","Tag","QueryItem","Struct","types","tags","Query","items","matchesQueryItem","event","item","typeOk","length","includes","type","eventTags","Set","every","tag","has","matchesQuery","query","some","matchesSequenced","sequenced","assertServableQuery","hasTaglessItem","Error","classifyServableQuery","kind","only","undefined","isNonEmptyReadonlyArray","fields","all","or"],"sources":["../../src/Query.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|