@kairos-es/core 1.0.0-alpha.0 → 1.0.0-alpha.1

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/src/Query.ts CHANGED
@@ -99,20 +99,50 @@ export const matchesSequenced = (
99
99
  * engine" behaviour.
100
100
  */
101
101
  export const assertServableQuery = (query: Query): void => {
102
+ const fault = servableQueryFault(query)
103
+ if (fault !== undefined) {
104
+ throw new Error(fault)
105
+ }
106
+ }
107
+
108
+ /**
109
+ * The servable grammar as a TOTAL function: the reason `query` cannot be served,
110
+ * or `undefined` when it can.
111
+ *
112
+ * The same rule as `assertServableQuery` above and NOT a second statement of it —
113
+ * that function is this one plus a throw, so the grammar is defined once and the
114
+ * two differ only in DISPOSITION. A store boundary wants the defect, because a
115
+ * non-servable query there is a programming error with no recovery; a caller that
116
+ * can still act on the answer wants a value, and the codec's read ledger is one:
117
+ * it asks whether a hop it is about to record would make the accumulated union
118
+ * unservable, and refuses on the error channel BEFORE the read is paid for.
119
+ *
120
+ * The alternative was for that caller to re-derive the rule, and it was tried:
121
+ * the codec's own copy tested each recorded query separately, which silently
122
+ * admitted a FIRST hop composed of two tagless slices — a multi-item query, and
123
+ * unservable, dying at the store one layer away. A grammar with two homes drifts
124
+ * at exactly the shapes neither author had in mind. The shape here is the read
125
+ * side's own (`projectionWiringFault` returns `string | undefined` for the same
126
+ * reason), so a fault is one exit route and one return type.
127
+ */
128
+ export const servableQueryFault = (query: Query): string | undefined => {
102
129
  const { items } = query
103
130
  // Wildcard and single-item queries are always servable.
104
131
  if (items.length <= 1) {
105
- return
132
+ return undefined
106
133
  }
107
134
  // A multi-item query is servable only if every item is tagged; a tagless
108
135
  // (type-only) item mixed with tagged items cannot ride a single index.
109
- const hasTaglessItem = items.some((item) => item.tags.length === 0)
110
- if (hasTaglessItem) {
111
- throw new Error(
112
- 'assertServableQuery: a multi-item query must have every item carry at ' +
113
- 'least one tag; a type-only item mixed with tagged items is not servable',
114
- )
115
- }
136
+ // The `assertServableQuery:` prefix is part of the MESSAGE and not something the
137
+ // throw adds, so both dispositions emit byte-identical text. It names the rule in
138
+ // the vocabulary a store rejects in, which is what lets the read side's wiring gate
139
+ // report a violation in the same words a store would — an author who meets it in
140
+ // both places meets it once. Building it here is also what stops a second caller
141
+ // re-deriving the prefix to match.
142
+ return items.some((item) => item.tags.length === 0)
143
+ ? 'assertServableQuery: a multi-item query must have every item carry at ' +
144
+ 'least one tag; a type-only item mixed with tagged items is not servable'
145
+ : undefined
116
146
  }
117
147
 
118
148
  /**
package/src/index.ts CHANGED
@@ -4,11 +4,15 @@
4
4
  *
5
5
  * Exposes the domain types (`DcbEvent`, `SequencedEvent`, `Position`, `Tag`,
6
6
  * `EventType`), the selection language (`Query`, `QueryItem`, the pure match
7
- * predicate + smart constructors, and the servable-shape dispatch every engine
8
- * compiles its plans through), the `AppendCondition`, tagged errors, the
9
- * `DcbEventStore` contract, the in-memory layer, the shared subscribe machine
7
+ * predicate + smart constructors, the servable grammar in both its dispositions —
8
+ * `assertServableQuery`'s throw and `servableQueryFault`'s total `string |
9
+ * undefined` — and the servable-shape dispatch every engine compiles its plans
10
+ * through), the `AppendCondition`, tagged errors, the
11
+ * `DcbEventStore` contract, `AppendIsOwned` and the refusal every engine's `append`
12
+ * inherits from it, the in-memory layer, the shared subscribe machine
10
13
  * every engine's `subscribe` is built from (`SubscribeMachine.ts`), the pure
11
- * projection primitives (`Projection`, `DecodedEvent`, `composeProjections`), and
14
+ * projection primitives (`Projection`, `DecodedEvent`, `composeProjections` and the
15
+ * `unionQueries` its query merge is reachable through without a record), and
12
16
  * `retryOnConflict`.
13
17
  *
14
18
  * Core keeps only the *pure* fold: projections fold an already-`DecodedEvent`
@@ -23,6 +27,7 @@
23
27
  * write-only consumer depend on a package it uses nothing else of (ADR-0008).
24
28
  */
25
29
  export * from './AppendCondition'
30
+ export * from './AppendIsOwned'
26
31
  export * from './ContractStore'
27
32
  export * from './DcbEventStore'
28
33
  export * from './Event'