@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.
Files changed (120) hide show
  1. package/README.md +3 -3
  2. package/dist/cjs/AppendCondition.js +1 -3
  3. package/dist/cjs/AppendCondition.js.map +1 -1
  4. package/dist/cjs/AppendIsOwned.js +6 -6
  5. package/dist/cjs/AppendIsOwned.js.map +1 -1
  6. package/dist/cjs/ContractStore.js +13 -4
  7. package/dist/cjs/ContractStore.js.map +1 -1
  8. package/dist/cjs/DcbEventStore.js +1 -1
  9. package/dist/cjs/DcbEventStore.js.map +1 -1
  10. package/dist/cjs/Event.js +87 -11
  11. package/dist/cjs/Event.js.map +1 -1
  12. package/dist/cjs/Projection.js +15 -9
  13. package/dist/cjs/Projection.js.map +1 -1
  14. package/dist/cjs/Query.js +59 -3
  15. package/dist/cjs/Query.js.map +1 -1
  16. package/dist/cjs/SubscribeMachine.js +20 -22
  17. package/dist/cjs/SubscribeMachine.js.map +1 -1
  18. package/dist/cjs/inMemory.js +113 -43
  19. package/dist/cjs/inMemory.js.map +1 -1
  20. package/dist/cjs/retryOnConflict.js +4 -10
  21. package/dist/cjs/retryOnConflict.js.map +1 -1
  22. package/dist/cjs/testing.js +2 -2
  23. package/dist/cjs/testing.js.map +1 -1
  24. package/dist/{dts → dts-cjs}/AppendCondition.d.ts +5 -7
  25. package/dist/{dts → dts-cjs}/AppendCondition.d.ts.map +1 -1
  26. package/dist/{dts → dts-cjs}/AppendIsOwned.d.ts +6 -6
  27. package/dist/{dts → dts-cjs}/AppendIsOwned.d.ts.map +1 -1
  28. package/dist/{dts → dts-cjs}/ContractStore.d.ts +7 -4
  29. package/dist/dts-cjs/ContractStore.d.ts.map +1 -0
  30. package/dist/{dts → dts-cjs}/DcbEventStore.d.ts +2 -2
  31. package/dist/{dts → dts-cjs}/DcbEventStore.d.ts.map +1 -1
  32. package/dist/dts-cjs/Event.d.ts +174 -0
  33. package/dist/dts-cjs/Event.d.ts.map +1 -0
  34. package/dist/{dts → dts-cjs}/Projection.d.ts +1 -1
  35. package/dist/{dts → dts-cjs}/Projection.d.ts.map +1 -1
  36. package/dist/{dts → dts-cjs}/Query.d.ts +19 -5
  37. package/dist/dts-cjs/Query.d.ts.map +1 -0
  38. package/dist/{dts → dts-cjs}/SubscribeMachine.d.ts +11 -15
  39. package/dist/dts-cjs/SubscribeMachine.d.ts.map +1 -0
  40. package/dist/{dts → dts-cjs}/inMemory.d.ts.map +1 -1
  41. package/dist/{dts → dts-cjs}/index.d.ts +1 -1
  42. package/dist/dts-cjs/package.json +3 -0
  43. package/dist/{dts → dts-cjs}/retryOnConflict.d.ts +1 -7
  44. package/dist/dts-cjs/retryOnConflict.d.ts.map +1 -0
  45. package/dist/{dts → dts-cjs}/testing.d.ts +1 -1
  46. package/dist/dts-esm/AppendCondition.d.ts +21 -0
  47. package/dist/dts-esm/AppendCondition.d.ts.map +1 -0
  48. package/dist/dts-esm/AppendIsOwned.d.ts +78 -0
  49. package/dist/dts-esm/AppendIsOwned.d.ts.map +1 -0
  50. package/dist/dts-esm/ContractStore.d.ts +62 -0
  51. package/dist/dts-esm/ContractStore.d.ts.map +1 -0
  52. package/dist/dts-esm/DcbEventStore.d.ts +64 -0
  53. package/dist/dts-esm/DcbEventStore.d.ts.map +1 -0
  54. package/dist/dts-esm/Event.d.ts +174 -0
  55. package/dist/dts-esm/Event.d.ts.map +1 -0
  56. package/dist/dts-esm/Projection.d.ts +124 -0
  57. package/dist/dts-esm/Projection.d.ts.map +1 -0
  58. package/dist/dts-esm/Query.d.ts +180 -0
  59. package/dist/dts-esm/Query.d.ts.map +1 -0
  60. package/dist/dts-esm/SubscribeMachine.d.ts +175 -0
  61. package/dist/dts-esm/SubscribeMachine.d.ts.map +1 -0
  62. package/dist/dts-esm/errors.d.ts +17 -0
  63. package/dist/dts-esm/errors.d.ts.map +1 -0
  64. package/dist/dts-esm/inMemory.d.ts +76 -0
  65. package/dist/dts-esm/inMemory.d.ts.map +1 -0
  66. package/dist/dts-esm/index.d.ts +40 -0
  67. package/dist/dts-esm/index.d.ts.map +1 -0
  68. package/dist/dts-esm/package.json +3 -0
  69. package/dist/dts-esm/retryOnConflict.d.ts +64 -0
  70. package/dist/dts-esm/retryOnConflict.d.ts.map +1 -0
  71. package/dist/dts-esm/testing.d.ts +39 -0
  72. package/dist/dts-esm/testing.d.ts.map +1 -0
  73. package/dist/esm/AppendCondition.js +1 -3
  74. package/dist/esm/AppendCondition.js.map +1 -1
  75. package/dist/esm/AppendIsOwned.js +6 -6
  76. package/dist/esm/AppendIsOwned.js.map +1 -1
  77. package/dist/esm/ContractStore.js +13 -4
  78. package/dist/esm/ContractStore.js.map +1 -1
  79. package/dist/esm/DcbEventStore.js +1 -1
  80. package/dist/esm/DcbEventStore.js.map +1 -1
  81. package/dist/esm/Event.js +87 -11
  82. package/dist/esm/Event.js.map +1 -1
  83. package/dist/esm/Projection.js +15 -9
  84. package/dist/esm/Projection.js.map +1 -1
  85. package/dist/esm/Query.js +56 -1
  86. package/dist/esm/Query.js.map +1 -1
  87. package/dist/esm/SubscribeMachine.js +21 -23
  88. package/dist/esm/SubscribeMachine.js.map +1 -1
  89. package/dist/esm/inMemory.js +114 -44
  90. package/dist/esm/inMemory.js.map +1 -1
  91. package/dist/esm/index.js +1 -1
  92. package/dist/esm/retryOnConflict.js +4 -10
  93. package/dist/esm/retryOnConflict.js.map +1 -1
  94. package/dist/esm/testing.js +2 -2
  95. package/dist/esm/testing.js.map +1 -1
  96. package/package.json +21 -11
  97. package/src/AppendCondition.ts +1 -1
  98. package/src/AppendIsOwned.ts +7 -7
  99. package/src/ContractStore.ts +25 -8
  100. package/src/DcbEventStore.ts +3 -3
  101. package/src/Event.ts +108 -11
  102. package/src/Projection.ts +19 -8
  103. package/src/Query.ts +74 -1
  104. package/src/SubscribeMachine.ts +38 -31
  105. package/src/inMemory.ts +123 -51
  106. package/src/index.ts +1 -1
  107. package/src/retryOnConflict.ts +6 -11
  108. package/src/testing.ts +2 -2
  109. package/testing/package.json +1 -1
  110. package/dist/dts/ContractStore.d.ts.map +0 -1
  111. package/dist/dts/Event.d.ts +0 -104
  112. package/dist/dts/Event.d.ts.map +0 -1
  113. package/dist/dts/Query.d.ts.map +0 -1
  114. package/dist/dts/SubscribeMachine.d.ts.map +0 -1
  115. package/dist/dts/retryOnConflict.d.ts.map +0 -1
  116. /package/dist/{dts → dts-cjs}/errors.d.ts +0 -0
  117. /package/dist/{dts → dts-cjs}/errors.d.ts.map +0 -0
  118. /package/dist/{dts → dts-cjs}/inMemory.d.ts +0 -0
  119. /package/dist/{dts → dts-cjs}/index.d.ts.map +0 -0
  120. /package/dist/{dts → dts-cjs}/testing.d.ts.map +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @kairos-es/core
2
2
 
3
- DCB-first event-sourcing contract and in-memory store for Effect v3.
3
+ DCB-first event-sourcing contract and in-memory store for Effect 4.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,12 +8,12 @@ DCB-first event-sourcing contract and in-memory store for Effect v3.
8
8
  Effect instance (per [ADR-0003](../../docs/adr)):
9
9
 
10
10
  ```sh
11
- pnpm add @kairos-es/core@alpha effect@^3.22.0
11
+ pnpm add @kairos-es/core@alpha effect@^4.0.5
12
12
  ```
13
13
 
14
14
  Provides the `DcbEventStore` contract (branded `Position` / `Tag` / `EventType`,
15
15
  the `DcbEvent` / `Query` / `AppendCondition` schemas, the errors and the
16
- `Context.Tag`), the pure `Projection` / `composeProjections` fold primitives
16
+ `Context.Service`), the pure `Projection` / `composeProjections` fold primitives
17
17
  (plus `unionQueries`, the OR-merge `composeProjections` folds a record through,
18
18
  reachable without one — a caller accumulating queries rather than projections
19
19
  needs it, and hand-rolling that merge silently drops a wildcard, an empty item
@@ -19,8 +19,6 @@ var _Query = require("./Query.js");
19
19
 
20
20
  const AppendCondition = exports.AppendCondition = /*#__PURE__*/_effect.Schema.Struct({
21
21
  failIfEventsMatch: _Query.Query,
22
- after: /*#__PURE__*/_effect.Schema.optionalWith(_Event.Position, {
23
- exact: true
24
- })
22
+ after: /*#__PURE__*/_effect.Schema.optionalKey(_Event.Position)
25
23
  });
26
24
  //# sourceMappingURL=AppendCondition.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppendCondition.js","names":["_effect","require","_Event","_Query","AppendCondition","exports","Schema","Struct","failIfEventsMatch","Query","after","optionalWith","Position","exact"],"sources":["../../src/AppendCondition.ts"],"sourcesContent":[null],"mappings":";;;;;;AASA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AAXA;;;;;;;;;;AAaO,MAAMG,eAAe,GAAAC,OAAA,CAAAD,eAAA,gBAAGE,cAAM,CAACC,MAAM,CAAC;EAC3CC,iBAAiB,EAAEC,YAAK;EACxBC,KAAK,eAAEJ,cAAM,CAACK,YAAY,CAACC,eAAQ,EAAE;IAAEC,KAAK,EAAE;EAAI,CAAE;CACrD,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"AppendCondition.js","names":["_effect","require","_Event","_Query","AppendCondition","exports","Schema","Struct","failIfEventsMatch","Query","after","optionalKey","Position"],"sources":["../../src/AppendCondition.ts"],"sourcesContent":[null],"mappings":";;;;;;AASA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AAXA;;;;;;;;;;AAaO,MAAMG,eAAe,GAAAC,OAAA,CAAAD,eAAA,gBAAGE,cAAM,CAACC,MAAM,CAAC;EAC3CC,iBAAiB,EAAEC,YAAK;EACxBC,KAAK,eAAEJ,cAAM,CAACK,WAAW,CAACC,eAAQ;CACnC,CAAC","ignoreList":[]}
@@ -37,15 +37,15 @@ var _effect = require("effect");
37
37
  *
38
38
  * ## Present means refused, so a sanctioned append REMOVES the marker
39
39
  *
40
- * The check reads the tag through `Effect.serviceOption`, so its ABSENCE is the
40
+ * The check reads the service through `Effect.serviceOption`, so its ABSENCE is the
41
41
  * normal case and costs the `append` signature nothing — `DcbEventStore.append`
42
42
  * keeps `R = never` and no caller provides anything. Presence is what refuses.
43
43
  *
44
44
  * That shape decides how the two legitimate appends inside a bracket get through,
45
45
  * and there is only one route: run with the marker STRIPPED from the context
46
- * (`Effect.mapInputContext` over `Context.omit(AppendIsOwned)`). The bracket's own
46
+ * (`Effect.updateContext` over `Context.omit(AppendIsOwned)`). The bracket's own
47
47
  * closing append is issued outside the scope it provided the marker in, and the
48
- * codec's escape hatch strips it explicitly. Neither needs a second tag, a
48
+ * codec's escape hatch strips it explicitly. Neither needs a second service, a
49
49
  * sanctioning flag or a mutable cell, and there is no way to be exempt by
50
50
  * accident: an append is either lexically outside the marked region or it says in
51
51
  * as many words that it is removing the marker.
@@ -56,10 +56,10 @@ var _effect = require("effect");
56
56
  * owns it.
57
57
  *
58
58
  * `by` is a human-readable owner name and exists for the defect message alone —
59
- * the refusal is decided by the tag's PRESENCE, never by this value, so nothing
59
+ * the refusal is decided by the service's PRESENCE, never by this value, so nothing
60
60
  * branches on it and no caller need agree on a vocabulary for it.
61
61
  */
62
- class AppendIsOwned extends /*#__PURE__*/_effect.Context.Tag('@kairos-es/core/AppendIsOwned')() {}
62
+ class AppendIsOwned extends /*#__PURE__*/_effect.Context.Service()('@kairos-es/core/AppendIsOwned') {}
63
63
  /**
64
64
  * Die if an append is already owned here; do nothing otherwise.
65
65
  *
@@ -72,7 +72,7 @@ class AppendIsOwned extends /*#__PURE__*/_effect.Context.Tag('@kairos-es/core/Ap
72
72
  * its escape hatch for a deliberate second append — which is what makes a defect
73
73
  * affordable here.
74
74
  *
75
- * `Effect.serviceOption` is what keeps this free: it reads the tag if present and
75
+ * `Effect.serviceOption` is what keeps this free: it reads the service if present and
76
76
  * adds NOTHING to the requirement channel, so every existing append is unchanged
77
77
  * and unprovisioned.
78
78
  */
@@ -1 +1 @@
1
- {"version":3,"file":"AppendIsOwned.js","names":["_effect","require","AppendIsOwned","Context","Tag","exports","assertAppendNotOwned","Effect","flatMap","serviceOption","owner","_tag","void","die","Error","value","by"],"sources":["../../src/AppendIsOwned.ts"],"sourcesContent":[null],"mappings":";;;;;;AA6CA,IAAAA,OAAA,GAAAC,OAAA;AA7CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA;;;;;;;;AAQM,MAAOC,aAAc,sBAAQC,eAAO,CAACC,GAAG,CAAC,+BAA+B,CAAC,EAG5E;AAEH;;;;;;;;;;;;;;;;AAAAC,OAAA,CAAAH,aAAA,GAAAA,aAAA;AAgBO,MAAMI,oBAAoB,GAAAD,OAAA,CAAAC,oBAAA,gBAAwBC,cAAM,CAACC,OAAO,cACrED,cAAM,CAACE,aAAa,CAACP,aAAa,CAAC,EAClCQ,KAAK,IACJA,KAAK,CAACC,IAAI,KAAK,MAAM,GACjBJ,cAAM,CAACK,IAAI,GACXL,cAAM,CAACM,GAAG,CACR,IAAIC,KAAK,CACP,kEAAkEJ,KAAK,CAACK,KAAK,CAACC,EAAE,MAAM,GACpF,4EAA4E,GAC5E,0EAA0E,GAC1E,4EAA4E,GAC5E,0EAA0E,GAC1E,sEAAsE,CACzE,CACF,CACR","ignoreList":[]}
1
+ {"version":3,"file":"AppendIsOwned.js","names":["_effect","require","AppendIsOwned","Context","Service","exports","assertAppendNotOwned","Effect","flatMap","serviceOption","owner","_tag","void","die","Error","value","by"],"sources":["../../src/AppendIsOwned.ts"],"sourcesContent":[null],"mappings":";;;;;;AA6CA,IAAAA,OAAA,GAAAC,OAAA;AA7CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA;;;;;;;;AAQM,MAAOC,aAAc,sBAAQC,eAAO,CAACC,OAAO,EAG/C,CAAC,+BAA+B,CAAC;AAEpC;;;;;;;;;;;;;;;;AAAAC,OAAA,CAAAH,aAAA,GAAAA,aAAA;AAgBO,MAAMI,oBAAoB,GAAAD,OAAA,CAAAC,oBAAA,gBAAwBC,cAAM,CAACC,OAAO,cACrED,cAAM,CAACE,aAAa,CAACP,aAAa,CAAC,EAClCQ,KAAK,IACJA,KAAK,CAACC,IAAI,KAAK,MAAM,GACjBJ,cAAM,CAACK,IAAI,GACXL,cAAM,CAACM,GAAG,CACR,IAAIC,KAAK,CACP,kEAAkEJ,KAAK,CAACK,KAAK,CAACC,EAAE,MAAM,GACpF,4EAA4E,GAC5E,0EAA0E,GAC1E,4EAA4E,GAC5E,0EAA0E,GAC1E,sEAAsE,CACzE,CACF,CACR","ignoreList":[]}
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.makeContractStore = void 0;
7
7
  var _effect = require("effect");
8
+ var _AppendCondition = require("./AppendCondition.js");
8
9
  var _AppendIsOwned = require("./AppendIsOwned.js");
9
10
  var _Event = require("./Event.js");
10
11
  var _Query = require("./Query.js");
@@ -26,8 +27,8 @@ const makeContractStore = raw => ({
26
27
  }),
27
28
  append: (events, condition) =>
28
29
  // The ownership refusal runs FIRST and sits OUTSIDE the suspend, because it is
29
- // the only one of append's four checks that is EFFECTFUL: it consults a
30
- // `Context.Tag` rather than inspecting the arguments. `Effect.flatMap` over an
30
+ // the only one of append's checks that is EFFECTFUL: it consults a
31
+ // `Context.Service` rather than inspecting the arguments. `Effect.flatMap` over an
31
32
  // already-lazy effect defers it exactly as the suspend defers the throws below,
32
33
  // so nothing runs at the call site either way; what it buys is that the
33
34
  // "you are inside a bracket" defect arrives before any argument is judged, which
@@ -47,8 +48,16 @@ const makeContractStore = raw => ({
47
48
  if (events.length === 0) {
48
49
  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.');
49
50
  }
50
- // A non-servable guard query is a defect in every engine.
51
+ // A non-servable guard query is a defect in every engine, and so is a
52
+ // condition carrying a key `AppendCondition` does not define: an author
53
+ // who wrote one believes they added a condition, and every engine would
54
+ // ignore it. Known keys come off the schema, as `servableQueryFault`
55
+ // reads its own (`Query.ts` owns the argument).
51
56
  if (condition !== undefined) {
57
+ const unknown = Object.keys(condition).find(key => !Object.hasOwn(_AppendCondition.AppendCondition.fields, key));
58
+ if (unknown !== undefined) {
59
+ throw new Error(`makeContractStore.append: the append condition carries the key ${JSON.stringify(unknown)}, ` + 'which `AppendCondition` does not define; an unknown key would be ' + 'silently ignored by every engine rather than honoured, so it is refused');
60
+ }
52
61
  (0, _Query.assertServableQuery)(condition.failIfEventsMatch);
53
62
  }
54
63
  // Only `AppendConditionFailed` belongs on the contract error channel;
@@ -74,7 +83,7 @@ const makeContractStore = raw => ({
74
83
  // engine's channel simply answers false and dies as it should. It also
75
84
  // narrows the raw `AppendConditionFailed | E` to the contract's single
76
85
  // error, which is what keeps `append` at `E = AppendConditionFailed`.
77
- return _effect.Effect.catchAll(raw.append(events, condition), error => _effect.Predicate.isTagged('AppendConditionFailed')(error) ? _effect.Effect.fail(error) : _effect.Effect.die(error));
86
+ return _effect.Effect.catch(raw.append(events, condition), error => _effect.Predicate.isTagged('AppendConditionFailed')(error) ? _effect.Effect.fail(error) : _effect.Effect.die(error));
78
87
  })),
79
88
  subscribe: (query, after) =>
80
89
  // `Effect.sync` runs when the stream is executed; a throw from
@@ -1 +1 @@
1
- {"version":3,"file":"ContractStore.js","names":["_effect","require","_AppendIsOwned","_Event","_Query","decodeReadLimit","Schema","decodeUnknownSync","ReadLimit","makeContractStore","raw","read","query","options","Effect","suspend","assertServableQuery","limit","undefined","orDie","append","events","condition","flatMap","assertAppendNotOwned","length","Error","failIfEventsMatch","catchAll","error","Predicate","isTagged","fail","die","subscribe","after","Stream","unwrap","sync","exports"],"sources":["../../src/ContractStore.ts"],"sourcesContent":[null],"mappings":";;;;;;AAmCA,IAAAA,OAAA,GAAAC,OAAA;AAEA,IAAAC,cAAA,GAAAD,OAAA;AAGA,IAAAE,MAAA,GAAAF,OAAA;AAEA,IAAAG,MAAA,GAAAH,OAAA;AAEA;AACA,MAAMI,eAAe,gBAAGC,cAAM,CAACC,iBAAiB,CAACC,gBAAS,CAAC;AAwB3D;;;;AAIO,MAAMC,iBAAiB,GAC5BC,GAAwB,KACG;EAC3BC,IAAI,EAAEA,CAACC,KAAK,EAAEC,OAAO,KACnBC,cAAM,CAACC,OAAO,CAAC,MAAK;IAClB;IACA,IAAAC,0BAAmB,EAACJ,KAAK,CAAC;IAC1B,IAAIC,OAAO,EAAEI,KAAK,KAAKC,SAAS,EAAE;MAChCb,eAAe,CAACQ,OAAO,CAACI,KAAK,CAAC;IAChC;IACA;IACA,OAAOH,cAAM,CAACK,KAAK,CAACT,GAAG,CAACC,IAAI,CAACC,KAAK,EAAEC,OAAO,CAAC,CAAC;EAC/C,CAAC,CAAC;EAEJO,MAAM,EAAEA,CAACC,MAAM,EAAEC,SAAS;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACAR,cAAM,CAACS,OAAO,CAACC,mCAAoB,EAAE,MACnCV,cAAM,CAACC,OAAO,CAAC,MAAK;IAClB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIM,MAAM,CAACI,MAAM,KAAK,CAAC,EAAE;MACvB,MAAM,IAAIC,KAAK,CACb,uEAAuE,GACrE,oEAAoE,GACpE,kEAAkE,CACrE;IACH;IACA;IACA,IAAIJ,SAAS,KAAKJ,SAAS,EAAE;MAC3B,IAAAF,0BAAmB,EAACM,SAAS,CAACK,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,OAAOb,cAAM,CAACc,QAAQ,CAAClB,GAAG,CAACU,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,EAAGO,KAAK,IAC1DC,iBAAS,CAACC,QAAQ,CAAC,uBAAuB,CAAC,CAACF,KAAK,CAAC,GAC9Cf,cAAM,CAACkB,IAAI,CAACH,KAAK,CAAC,GAClBf,cAAM,CAACmB,GAAG,CAACJ,KAAK,CAAC,CACtB;EACH,CAAC,CAAC,CACH;EAEHK,SAAS,EAAEA,CAACtB,KAAK,EAAEuB,KAAK;EACtB;EACA;EACA;EACA;EACAC,cAAM,CAACC,MAAM,CACXvB,cAAM,CAACwB,IAAI,CAAC,MAAK;IACf,IAAAtB,0BAAmB,EAACJ,KAAK,CAAC;IAC1B,OAAOwB,cAAM,CAACjB,KAAK,CAACT,GAAG,CAACwB,SAAS,CAACtB,KAAK,EAAEuB,KAAK,CAAC,CAAC;EAClD,CAAC,CAAC;CAEP,CAAC;AAAAI,OAAA,CAAA9B,iBAAA,GAAAA,iBAAA","ignoreList":[]}
1
+ {"version":3,"file":"ContractStore.js","names":["_effect","require","_AppendCondition","_AppendIsOwned","_Event","_Query","decodeReadLimit","Schema","decodeUnknownSync","ReadLimit","makeContractStore","raw","read","query","options","Effect","suspend","assertServableQuery","limit","undefined","orDie","append","events","condition","flatMap","assertAppendNotOwned","length","Error","unknown","Object","keys","find","key","hasOwn","AppendCondition","fields","JSON","stringify","failIfEventsMatch","catch","error","Predicate","isTagged","fail","die","subscribe","after","Stream","unwrap","sync","exports"],"sources":["../../src/ContractStore.ts"],"sourcesContent":[null],"mappings":";;;;;;AAsCA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,cAAA,GAAAF,OAAA;AAGA,IAAAG,MAAA,GAAAH,OAAA;AAEA,IAAAI,MAAA,GAAAJ,OAAA;AAEA;AACA,MAAMK,eAAe,gBAAGC,cAAM,CAACC,iBAAiB,CAACC,gBAAS,CAAC;AAwB3D;;;;AAIO,MAAMC,iBAAiB,GAC5BC,GAAwB,KACM;EAC9BC,IAAI,EAAEA,CAACC,KAAK,EAAEC,OAAO,KACnBC,cAAM,CAACC,OAAO,CAAC,MAAK;IAClB;IACA,IAAAC,0BAAmB,EAACJ,KAAK,CAAC;IAC1B,IAAIC,OAAO,EAAEI,KAAK,KAAKC,SAAS,EAAE;MAChCb,eAAe,CAACQ,OAAO,CAACI,KAAK,CAAC;IAChC;IACA;IACA,OAAOH,cAAM,CAACK,KAAK,CAACT,GAAG,CAACC,IAAI,CAACC,KAAK,EAAEC,OAAO,CAAC,CAAC;EAC/C,CAAC,CAAC;EAEJO,MAAM,EAAEA,CAACC,MAAM,EAAEC,SAAS;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACAR,cAAM,CAACS,OAAO,CAACC,mCAAoB,EAAE,MACnCV,cAAM,CAACC,OAAO,CAAC,MAAK;IAClB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIM,MAAM,CAACI,MAAM,KAAK,CAAC,EAAE;MACvB,MAAM,IAAIC,KAAK,CACb,uEAAuE,GACrE,oEAAoE,GACpE,kEAAkE,CACrE;IACH;IACA;IACA;IACA;IACA;IACA;IACA,IAAIJ,SAAS,KAAKJ,SAAS,EAAE;MAC3B,MAAMS,OAAO,GAAGC,MAAM,CAACC,IAAI,CAACP,SAAS,CAAC,CAACQ,IAAI,CACxCC,GAAG,IAAK,CAACH,MAAM,CAACI,MAAM,CAACC,gCAAe,CAACC,MAAM,EAAEH,GAAG,CAAC,CACrD;MACD,IAAIJ,OAAO,KAAKT,SAAS,EAAE;QACzB,MAAM,IAAIQ,KAAK,CACb,kEAAkES,IAAI,CAACC,SAAS,CAACT,OAAO,CAAC,IAAI,GAC3F,mEAAmE,GACnE,yEAAyE,CAC5E;MACH;MACA,IAAAX,0BAAmB,EAACM,SAAS,CAACe,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,OAAOvB,cAAM,CAACwB,KAAK,CAAC5B,GAAG,CAACU,MAAM,CAACC,MAAM,EAAEC,SAAS,CAAC,EAAGiB,KAAK,IACvDC,iBAAS,CAACC,QAAQ,CAAC,uBAAuB,CAAC,CAACF,KAAK,CAAC,GAC9CzB,cAAM,CAAC4B,IAAI,CAACH,KAAK,CAAC,GAClBzB,cAAM,CAAC6B,GAAG,CAACJ,KAAK,CAAC,CACtB;EACH,CAAC,CAAC,CACH;EAEHK,SAAS,EAAEA,CAAChC,KAAK,EAAEiC,KAAK;EACtB;EACA;EACA;EACA;EACAC,cAAM,CAACC,MAAM,CACXjC,cAAM,CAACkC,IAAI,CAAC,MAAK;IACf,IAAAhC,0BAAmB,EAACJ,KAAK,CAAC;IAC1B,OAAOkC,cAAM,CAAC3B,KAAK,CAACT,GAAG,CAACkC,SAAS,CAAChC,KAAK,EAAEiC,KAAK,CAAC,CAAC;EAClD,CAAC,CAAC;CAEP,CAAC;AAAAI,OAAA,CAAAxC,iBAAA,GAAAA,iBAAA","ignoreList":[]}
@@ -5,6 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.DcbEventStore = void 0;
7
7
  var _effect = require("effect");
8
- class DcbEventStore extends /*#__PURE__*/_effect.Context.Tag('@kairos-es/core/DcbEventStore')() {}
8
+ class DcbEventStore extends /*#__PURE__*/_effect.Context.Service()('@kairos-es/core/DcbEventStore') {}
9
9
  exports.DcbEventStore = DcbEventStore;
10
10
  //# sourceMappingURL=DcbEventStore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DcbEventStore.js","names":["_effect","require","DcbEventStore","Context","Tag","exports"],"sources":["../../src/DcbEventStore.ts"],"sourcesContent":[null],"mappings":";;;;;;AAiBA,IAAAA,OAAA,GAAAC,OAAA;AA2BM,MAAOC,aAAc,sBAAQC,eAAO,CAACC,GAAG,CAAC,+BAA+B,CAAC,EAgC5E;AAAAC,OAAA,CAAAH,aAAA,GAAAA,aAAA","ignoreList":[]}
1
+ {"version":3,"file":"DcbEventStore.js","names":["_effect","require","DcbEventStore","Context","Service","exports"],"sources":["../../src/DcbEventStore.ts"],"sourcesContent":[null],"mappings":";;;;;;AAiBA,IAAAA,OAAA,GAAAC,OAAA;AA2BM,MAAOC,aAAc,sBAAQC,eAAO,CAACC,OAAO,EAgC/C,CAAC,+BAA+B,CAAC;AAAAC,OAAA,CAAAH,aAAA,GAAAA,aAAA","ignoreList":[]}
package/dist/cjs/Event.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.tagIdentity = exports.Tag = exports.SequencedEvent = exports.ReadLimit = exports.Position = exports.ORIGIN = exports.EventType = exports.DcbEvent = void 0;
6
+ exports.tagIdentity = exports.Tag = exports.SequencedEvent = exports.ReadLimit = exports.Position = exports.ORIGIN = exports.Metadata = exports.EventType = exports.DcbEvent = void 0;
7
7
  var _effect = require("effect");
8
8
  /**
9
9
  * Core domain types for the DCB event log: branded scalars and the wire
@@ -19,11 +19,28 @@ var _effect = require("effect");
19
19
  *
20
20
  * The log is 64-bit (`bigserial` in Postgres) and exceeds JS `number` safety at
21
21
  * 2^53, so positions are a `bigint`. They are compared, never arithmetic'd.
22
- * `Schema.BigInt` encodes to a decimal string (JSON-safe) and decodes to
22
+ * Encoded, a position is a decimal string (JSON-safe, `String(b)`); decoded, a
23
23
  * `bigint`. `0n` is the "before the first event" sentinel used by `after` and by
24
24
  * an empty store's head.
25
+ *
26
+ * The string-to-`bigint` step is hand-built rather than Effect 4's
27
+ * `Schema.BigIntFromString`, because that schema accepts only `^-?\d+$` and so
28
+ * refuses inputs the encoded side has always admitted — `" 5"`, `"+5"`,
29
+ * `"0x10"`. The decode here is the global `BigInt(s)` behind a blank-string
30
+ * refusal, which is `BigInt.fromString` and is exactly what Effect 3's
31
+ * `Schema.BigInt` ran, so a position string any earlier release decoded still
32
+ * decodes to the same value. The non-negative check then runs on the `bigint`,
33
+ * as it always did.
25
34
  */
26
- const Position = exports.Position = /*#__PURE__*/_effect.Schema.BigInt.pipe(/*#__PURE__*/_effect.Schema.nonNegativeBigInt(), /*#__PURE__*/_effect.Schema.brand('Position'));
35
+ const Position = exports.Position = /*#__PURE__*/_effect.Schema.String.pipe(/*#__PURE__*/_effect.Schema.decodeTo(_effect.Schema.BigInt, /*#__PURE__*/_effect.SchemaTransformation.transformEffect({
36
+ decode: (s, options) => _effect.Option.match(_effect.BigInt.fromString(s), {
37
+ onNone: () => _effect.Effect.fail(new _effect.SchemaIssue.InvalidValue({
38
+ message: `Unable to decode ${JSON.stringify(s)} into a bigint`
39
+ }, s, options)),
40
+ onSome: _effect.Effect.succeed
41
+ }),
42
+ encode: b => _effect.Effect.succeed(String(b))
43
+ })), /*#__PURE__*/_effect.Schema.check(/*#__PURE__*/_effect.Schema.isGreaterThanOrEqualToBigInt(0n)), /*#__PURE__*/_effect.Schema.brand('Position'));
27
44
  /** The sentinel position meaning "before the first event". */
28
45
  const ORIGIN = exports.ORIGIN = /*#__PURE__*/Position.make(0n);
29
46
  /**
@@ -32,9 +49,9 @@ const ORIGIN = exports.ORIGIN = /*#__PURE__*/Position.make(0n);
32
49
  * event sourcing; a query matches an event when the event carries all of an
33
50
  * item's tags (superset test).
34
51
  */
35
- const Tag = exports.Tag = /*#__PURE__*/_effect.Schema.String.pipe(/*#__PURE__*/_effect.Schema.pattern(/^[^:]+:.+/, {
52
+ const Tag = exports.Tag = /*#__PURE__*/_effect.Schema.String.pipe(/*#__PURE__*/_effect.Schema.check(/*#__PURE__*/_effect.Schema.isPattern(/^[^:]+:.+/, {
36
53
  identifier: 'Tag'
37
- }), /*#__PURE__*/_effect.Schema.brand('Tag'));
54
+ })), /*#__PURE__*/_effect.Schema.brand('Tag'));
38
55
  const tagIdentity = key => {
39
56
  if (key.length === 0 || key.includes(':')) {
40
57
  throw new Error(`tagIdentity: key must be non-empty and contain no ':' (got ${JSON.stringify(key)})`);
@@ -60,26 +77,85 @@ const tagIdentity = key => {
60
77
  * `ReadLimit.make(n)`.
61
78
  */
62
79
  exports.tagIdentity = tagIdentity;
63
- const ReadLimit = exports.ReadLimit = /*#__PURE__*/_effect.Schema.Int.pipe(/*#__PURE__*/_effect.Schema.greaterThanOrEqualTo(0), /*#__PURE__*/_effect.Schema.brand('ReadLimit'));
80
+ const ReadLimit = exports.ReadLimit = /*#__PURE__*/_effect.Schema.Int.pipe(/*#__PURE__*/_effect.Schema.check(/*#__PURE__*/_effect.Schema.isGreaterThanOrEqualTo(0)), /*#__PURE__*/_effect.Schema.brand('ReadLimit'));
64
81
  /**
65
82
  * An event type name, a branded non-empty string. Versioning is by convention
66
83
  * in the name (e.g. `course-registered.v2`); only non-emptiness is enforced.
67
84
  */
68
- const EventType = exports.EventType = /*#__PURE__*/_effect.Schema.String.pipe(/*#__PURE__*/_effect.Schema.nonEmptyString(), /*#__PURE__*/_effect.Schema.brand('EventType'));
85
+ const EventType = exports.EventType = /*#__PURE__*/_effect.Schema.String.pipe(/*#__PURE__*/_effect.Schema.check(/*#__PURE__*/_effect.Schema.isNonEmpty()), /*#__PURE__*/_effect.Schema.brand('EventType'));
86
+ /**
87
+ * An event's metadata: a flat bag of strings carrying the circumstances under
88
+ * which the event was RECORDED, never anything the business asserts (ADR-0017).
89
+ * Payload is what happened; metadata is the context of the recording — a
90
+ * `traceparent`, a correlation or causation id, a deployment revision.
91
+ *
92
+ * Values are strings, not `unknown`, for cross-engine parity rather than taste:
93
+ * both SQL engines carry the bag as a JSON document, and `JSON.stringify` drops
94
+ * `undefined`, throws on `bigint` and turns `NaN` into `null`, none of which the
95
+ * in-memory oracle does.
96
+ *
97
+ * The key schema carries no refinement, deliberately: a refinement on a
98
+ * `Schema.Record` key acts as a FILTER on decode, silently dropping a key that
99
+ * fails it rather than failing the decode. Constraining keys, if ever wanted,
100
+ * has to be a validating step of its own.
101
+ *
102
+ * Two engine limits a consumer must know, because the bag is typically filled
103
+ * from outside the application at a request edge. Postgres refuses a NUL
104
+ * character (U+0000) and an unpaired surrogate in any string it stores, so an
105
+ * event whose bag holds one fails to append there while the in-memory and
106
+ * SQLite engines accept it; sanitise untrusted values before they reach the
107
+ * bag. And key ORDER is not preserved: Postgres stores an object's keys in an
108
+ * order of its own, so compare bags as unordered and never rely on the order
109
+ * `Object.keys` returns.
110
+ *
111
+ * Metadata is never part of a `Query` or an `AppendCondition` — a store refuses
112
+ * either when it carries a key the grammar does not define, so a metadata
113
+ * predicate cannot be written and silently ignored — never reaches a codec's
114
+ * `tagsFor`, and no store offers a way to rewrite it once appended. ADR-0017
115
+ * owns why each of those is mechanical rather than preferential.
116
+ *
117
+ * ## The one home of the shape rule
118
+ *
119
+ * The bag is a JSON OBJECT whose every value is a STRING, and this schema is
120
+ * where that rule is stated. At runtime it is checked in ONE place, at write: the
121
+ * codec's `toEvent` decodes every bag through this schema and refuses one that
122
+ * fails on its own error channel, so a value smuggled past the type (through an
123
+ * `any`) stops there. The schema judges a bag's OWN enumerable keys, so an object
124
+ * that keeps its entries anywhere else — a `Map`, a `Date` — reads as the empty
125
+ * bag rather than failing. A store checks no values: an event built by hand and handed
126
+ * to `append` directly carries whatever it carries, and what each engine then does
127
+ * with a non-string value differs — one may store it and read it back differently
128
+ * from another, and a `bigint` makes a SQL engine's append fail. Each
129
+ * SQL engine keeps a container check of its own on read — the column must hold a
130
+ * JSON object — because that defends against a writer outside the library and is
131
+ * about the column, not the values. A VALUE check must never be added on read
132
+ * alone: it would make an event the store accepted unreadable for ever, so a
133
+ * value check belongs at write or nowhere.
134
+ */
135
+ const Metadata = exports.Metadata = /*#__PURE__*/_effect.Schema.Record(_effect.Schema.String, _effect.Schema.String);
69
136
  /**
70
137
  * An event as stored: an opaque payload plus its type, tags, a
71
- * producer-supplied `uuid`, and producer-supplied `occurredAt` domain time.
138
+ * producer-supplied `uuid`, producer-supplied `occurredAt` domain time, and its
139
+ * `metadata` bag.
72
140
  *
73
141
  * `data` is `Uint8Array` (opaque bytes); the store does not interpret it.
74
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.
75
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.
76
151
  */
77
152
  const DcbEvent = exports.DcbEvent = /*#__PURE__*/_effect.Schema.Struct({
78
153
  type: EventType,
79
- data: _effect.Schema.Uint8ArrayFromSelf,
154
+ data: _effect.Schema.Uint8Array,
80
155
  tags: /*#__PURE__*/_effect.Schema.Array(Tag),
81
- uuid: _effect.Schema.UUID,
82
- occurredAt: _effect.Schema.DateTimeUtc
156
+ uuid: /*#__PURE__*/_effect.Schema.String.check(/*#__PURE__*/_effect.Schema.isGUID()),
157
+ occurredAt: _effect.Schema.DateTimeUtcFromString,
158
+ metadata: Metadata
83
159
  });
84
160
  /** A persisted event: the event together with the position it was written at. */
85
161
  const SequencedEvent = exports.SequencedEvent = /*#__PURE__*/_effect.Schema.Struct({
@@ -1 +1 @@
1
- {"version":3,"file":"Event.js","names":["_effect","require","Position","exports","Schema","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":";;;;;;AAQA,IAAAA,OAAA,GAAAC,OAAA;AARA;;;;;;;;;AAUA;;;;;;;;;AASO,MAAMC,QAAQ,GAAAC,OAAA,CAAAD,QAAA,gBAAGE,cAAM,CAACC,MAAM,CAACC,IAAI,cACxCF,cAAM,CAACG,iBAAiB,EAAE,eAC1BH,cAAM,CAACI,KAAK,CAAC,UAAU,CAAC,CACzB;AAGD;AACO,MAAMC,MAAM,GAAAN,OAAA,CAAAM,MAAA,gBAAaP,QAAQ,CAACQ,IAAI,CAAC,EAAE,CAAC;AAEjD;;;;;;AAMO,MAAMC,GAAG,GAAAR,OAAA,CAAAQ,GAAA,gBAAGP,cAAM,CAACQ,MAAM,CAACN,IAAI,cACnCF,cAAM,CAACS,OAAO,CAAC,WAAW,EAAE;EAAEC,UAAU,EAAE;AAAK,CAAE,CAAC,eAClDV,cAAM,CAACI,KAAK,CAAC,KAAK,CAAC,CACpB;AA8BM,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;;;;;;;;;;AAAAnB,OAAA,CAAAY,WAAA,GAAAA,WAAA;AAUO,MAAMQ,SAAS,GAAApB,OAAA,CAAAoB,SAAA,gBAAGnB,cAAM,CAACoB,GAAG,CAAClB,IAAI,cACtCF,cAAM,CAACqB,oBAAoB,CAAC,CAAC,CAAC,eAC9BrB,cAAM,CAACI,KAAK,CAAC,WAAW,CAAC,CAC1B;AAGD;;;;AAIO,MAAMkB,SAAS,GAAAvB,OAAA,CAAAuB,SAAA,gBAAGtB,cAAM,CAACQ,MAAM,CAACN,IAAI,cACzCF,cAAM,CAACuB,cAAc,EAAE,eACvBvB,cAAM,CAACI,KAAK,CAAC,WAAW,CAAC,CAC1B;AAGD;;;;;;;;AAQO,MAAMoB,QAAQ,GAAAzB,OAAA,CAAAyB,QAAA,gBAAGxB,cAAM,CAACyB,MAAM,CAAC;EACpCC,IAAI,EAAEJ,SAAS;EACfK,IAAI,EAAE3B,cAAM,CAAC4B,kBAAkB;EAC/BC,IAAI,eAAE7B,cAAM,CAAC8B,KAAK,CAACvB,GAAG,CAAC;EACvBwB,IAAI,EAAE/B,cAAM,CAACgC,IAAI;EACjBC,UAAU,EAAEjC,cAAM,CAACkC;CACpB,CAAC;AAGF;AACO,MAAMC,cAAc,GAAApC,OAAA,CAAAoC,cAAA,gBAAGnC,cAAM,CAACyB,MAAM,CAAC;EAC1CW,KAAK,EAAEZ,QAAQ;EACfa,QAAQ,EAAEvC;CACX,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"Event.js","names":["_effect","require","Position","exports","Schema","String","pipe","decodeTo","BigInt","SchemaTransformation","transformEffect","decode","s","options","Option","match","BigInt_","fromString","onNone","Effect","fail","SchemaIssue","InvalidValue","message","JSON","stringify","onSome","succeed","encode","b","check","isGreaterThanOrEqualToBigInt","brand","ORIGIN","make","Tag","isPattern","identifier","tagIdentity","key","length","includes","Error","value","ReadLimit","Int","isGreaterThanOrEqualTo","EventType","isNonEmpty","Metadata","Record","DcbEvent","Struct","type","data","Uint8Array","tags","Array","uuid","isGUID","occurredAt","DateTimeUtcFromString","metadata","SequencedEvent","event","position"],"sources":["../../src/Event.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -24,15 +24,21 @@ const AnyProjection = exports.AnyProjection = {
24
24
  */
25
25
  of: projection => projection
26
26
  };
27
- /** Merge two queries by OR-ing their items; either wildcard yields a wildcard. */
28
- const mergeQuery = (left, right) => {
29
- if (left.items.length === 0 || right.items.length === 0) {
30
- return WILDCARD;
31
- }
32
- return {
33
- items: [...left.items, ...right.items]
34
- };
35
- };
27
+ /**
28
+ * Merge two queries by OR-ing their items; either wildcard yields a wildcard.
29
+ *
30
+ * Every other key on either envelope is carried into the result. The grammar's
31
+ * envelope has no key but `items`, so anything else is a key the store boundary
32
+ * refuses (`servableQueryFault`); a merge that rebuilt the envelope from `items`
33
+ * alone would drop that key before the boundary saw it, and a slice composed with
34
+ * another would then have its smuggled predicate ignored in silence — the outcome
35
+ * the refusal exists to prevent.
36
+ */
37
+ const mergeQuery = (left, right) => ({
38
+ ...left,
39
+ ...right,
40
+ items: left.items.length === 0 || right.items.length === 0 ? WILDCARD.items : [...left.items, ...right.items]
41
+ });
36
42
  /**
37
43
  * OR-merge a list of queries into one. The empty list is the wildcard.
38
44
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Projection.js","names":["_Query","require","WILDCARD","items","AnyProjection","exports","of","projection","mergeQuery","left","right","length","unionQueries","queries","reduce","acc","q","composeProjections","projections","keys","Object","initialState","fromEntries","map","key","mergedQuery","query","evolve","state","decoded","next","matchesSequenced","sequenced"],"sources":["../../src/Projection.ts"],"sourcesContent":[null],"mappings":";;;;;;AA4BA,IAAAA,MAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,QAAQ,GAAU;EAAEC,KAAK,EAAE;AAAE,CAAE;AAqD9B,MAAMC,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAG;EAC3B;;;;;;;EAOAE,EAAE,EAAMC,UAAyB,IAC/BA;CACM;AAaV;AACA,MAAMC,UAAU,GAAGA,CAACC,IAAW,EAAEC,KAAY,KAAW;EACtD,IAAID,IAAI,CAACN,KAAK,CAACQ,MAAM,KAAK,CAAC,IAAID,KAAK,CAACP,KAAK,CAACQ,MAAM,KAAK,CAAC,EAAE;IACvD,OAAOT,QAAQ;EACjB;EACA,OAAO;IAAEC,KAAK,EAAE,CAAC,GAAGM,IAAI,CAACN,KAAK,EAAE,GAAGO,KAAK,CAACP,KAAK;EAAC,CAAE;AACnD,CAAC;AAED;;;;;;;;;;;;;;;AAeO,MAAMS,YAAY,GAAIC,OAA6B,IACxDA,OAAO,CAACF,MAAM,KAAK,CAAC,GAChBT,QAAQ,GACRW,OAAO,CAACC,MAAM,CAAC,CAACC,GAAG,EAAEC,CAAC,KAAKR,UAAU,CAACO,GAAG,EAAEC,CAAC,CAAC,CAAC;AAEpD;;;;;;;;;;;AAAAX,OAAA,CAAAO,YAAA,GAAAA,YAAA;AAWO,MAAMK,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,MAAMI,WAAW,GAAGb,YAAY,CAACO,IAAI,CAACI,GAAG,CAAEC,GAAG,IAAKN,WAAW,CAACM,GAAG,CAAC,CAACE,KAAK,CAAC,CAAC;EAE3E,OAAO;IACLL,YAAY;IACZK,KAAK,EAAED,WAAW;IAClBE,MAAM,EAAEA,CAACC,KAAK,EAAEC,OAAO,KAAI;MACzB,MAAMC,IAAI,GAAG;QAAE,GAAGF;MAAK,CAAuC;MAC9D,KAAK,MAAMJ,GAAG,IAAIL,IAAI,EAAE;QACtB,MAAMZ,UAAU,GAAGW,WAAW,CAACM,GAAG,CAAC;QACnC,IAAI,CAAC,IAAAO,uBAAgB,EAACF,OAAO,CAACG,SAAS,EAAEzB,UAAU,CAACmB,KAAK,CAAC,EAAE;UAC1D;QACF;QACAI,IAAI,CAACN,GAAG,CAAC,GAAGjB,UAAU,CAACoB,MAAM,CAACC,KAAK,CAACJ,GAAG,CAAC,EAAEK,OAAO,CAEhD;MACH;MACA,OAAOC,IAAI;IACb;GACD;AACH,CAAC;AAAAzB,OAAA,CAAAY,kBAAA,GAAAA,kBAAA","ignoreList":[]}
1
+ {"version":3,"file":"Projection.js","names":["_Query","require","WILDCARD","items","AnyProjection","exports","of","projection","mergeQuery","left","right","length","unionQueries","queries","reduce","acc","q","composeProjections","projections","keys","Object","initialState","fromEntries","map","key","mergedQuery","query","evolve","state","decoded","next","matchesSequenced","sequenced"],"sources":["../../src/Projection.ts"],"sourcesContent":[null],"mappings":";;;;;;AA4BA,IAAAA,MAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,QAAQ,GAAU;EAAEC,KAAK,EAAE;AAAE,CAAE;AAqD9B,MAAMC,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAG;EAC3B;;;;;;;EAOAE,EAAE,EAAMC,UAAyB,IAC/BA;CACM;AAaV;;;;;;;;;;AAUA,MAAMC,UAAU,GAAGA,CAACC,IAAW,EAAEC,KAAY,MAAa;EACxD,GAAGD,IAAI;EACP,GAAGC,KAAK;EACRP,KAAK,EACHM,IAAI,CAACN,KAAK,CAACQ,MAAM,KAAK,CAAC,IAAID,KAAK,CAACP,KAAK,CAACQ,MAAM,KAAK,CAAC,GAC/CT,QAAQ,CAACC,KAAK,GACd,CAAC,GAAGM,IAAI,CAACN,KAAK,EAAE,GAAGO,KAAK,CAACP,KAAK;CACrC,CAAC;AAEF;;;;;;;;;;;;;;;AAeO,MAAMS,YAAY,GAAIC,OAA6B,IACxDA,OAAO,CAACF,MAAM,KAAK,CAAC,GAChBT,QAAQ,GACRW,OAAO,CAACC,MAAM,CAAC,CAACC,GAAG,EAAEC,CAAC,KAAKR,UAAU,CAACO,GAAG,EAAEC,CAAC,CAAC,CAAC;AAEpD;;;;;;;;;;;AAAAX,OAAA,CAAAO,YAAA,GAAAA,YAAA;AAWO,MAAMK,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,MAAMI,WAAW,GAAGb,YAAY,CAACO,IAAI,CAACI,GAAG,CAAEC,GAAG,IAAKN,WAAW,CAACM,GAAG,CAAC,CAACE,KAAK,CAAC,CAAC;EAE3E,OAAO;IACLL,YAAY;IACZK,KAAK,EAAED,WAAW;IAClBE,MAAM,EAAEA,CAACC,KAAK,EAAEC,OAAO,KAAI;MACzB,MAAMC,IAAI,GAAG;QAAE,GAAGF;MAAK,CAAuC;MAC9D,KAAK,MAAMJ,GAAG,IAAIL,IAAI,EAAE;QACtB,MAAMZ,UAAU,GAAGW,WAAW,CAACM,GAAG,CAAC;QACnC,IAAI,CAAC,IAAAO,uBAAgB,EAACF,OAAO,CAACG,SAAS,EAAEzB,UAAU,CAACmB,KAAK,CAAC,EAAE;UAC1D;QACF;QACAI,IAAI,CAACN,GAAG,CAAC,GAAGjB,UAAU,CAACoB,MAAM,CAACC,KAAK,CAACJ,GAAG,CAAC,EAAEK,OAAO,CAEhD;MACH;MACA,OAAOC,IAAI;IACb;GACD;AACH,CAAC;AAAAzB,OAAA,CAAAY,kBAAA,GAAAA,kBAAA","ignoreList":[]}
package/dist/cjs/Query.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.servableQueryFault = exports.query = exports.matchesSequenced = exports.matchesQueryItem = exports.matchesQuery = exports.classifyServableQuery = exports.assertServableQuery = exports.QueryItem = exports.Query = void 0;
6
+ exports.unknownQueryKeyFault = exports.servableQueryFault = exports.query = exports.matchesSequenced = exports.matchesQueryItem = exports.matchesQuery = exports.classifyServableQuery = exports.assertServableQuery = exports.QueryItem = exports.Query = void 0;
7
7
  var _effect = require("effect");
8
8
  var _Event = require("./Event.js");
9
9
  /**
@@ -77,6 +77,9 @@ const matchesSequenced = (sequenced, query) => matchesQuery(sequenced.event, que
77
77
  * (`Query.ts` module docs) but which plain-data assembly — e.g. decision-model
78
78
  * composition, `Projection.ts:31-36` — can still bypass.
79
79
  *
80
+ * A query, or any of its items, carrying a key the grammar does not define is
81
+ * refused before anything else is judged (`unknownKeyFault` below says why).
82
+ *
80
83
  * The servable grammar is exactly one of:
81
84
  * - the wildcard (`items.length === 0`), which matches every event;
82
85
  * - a *single* item (`items.length === 1`), any types/tags, including a
@@ -103,6 +106,55 @@ const assertServableQuery = query => {
103
106
  throw new Error(fault);
104
107
  }
105
108
  };
109
+ /**
110
+ * The refusal of a key `schemaFields` does not define, or `undefined` when `value`
111
+ * carries none. `where` names the level for the message.
112
+ *
113
+ * ## Why an unknown key is refused rather than ignored
114
+ *
115
+ * A `Query` is plain data, so nothing at runtime stops a caller adding a key the
116
+ * grammar does not have — a `metadata` predicate on an item is the one this repo
117
+ * expects, since metadata is the field an author most plausibly wants to filter on
118
+ * and must not (ADR-0017). Ignored, that key is a predicate the author believes they
119
+ * wrote and no engine honours, and on the append side it is worse: the Postgres
120
+ * guard embeds its match as literal plpgsql text that no type reaches, so a key a
121
+ * read path one day learned to honour would still be dropped by the guard, and the
122
+ * optimistic-concurrency check would escape in silence. Refusing every key the
123
+ * grammar does not define closes that route — a SMUGGLED key — at the one boundary
124
+ * every engine shares, before any engine could disagree. "Every key" means every
125
+ * key `Object.keys` returns: an own, enumerable, string key, which is what a plain
126
+ * object literal or a spread builds. A symbol key, a non-enumerable key or an
127
+ * inherited one is not judged, and no engine reads one either. Core's own query
128
+ * merge (`unionQueries`, `composeProjections`) carries an envelope's unknown key
129
+ * into the merged query rather than dropping it, so composing slices cannot launder
130
+ * a smuggled key past this check.
131
+ *
132
+ * It does not close the neighbouring one, and cannot: a field ADDED to the grammar's
133
+ * schema is a known key from that edit on and passes here, while the plpgsql guard
134
+ * still has to be taught it by hand. That exposure is the Postgres guard's, and its
135
+ * own comment in that engine's DDL says so.
136
+ *
137
+ * The known keys are read off the SCHEMA's own `fields` rather than listed here, so
138
+ * a field added to the grammar is admitted by the same edit that adds it. Only key
139
+ * NAMES are judged: an exact-optional key written as `undefined` is a known key and
140
+ * passes, because which values a key admits is the type's business, not this rule's.
141
+ */
142
+ exports.assertServableQuery = assertServableQuery;
143
+ const unknownKeyFault = (where, value, schemaFields) => {
144
+ const unknown = Object.keys(value).find(key => !Object.hasOwn(schemaFields, key));
145
+ return unknown === undefined ? undefined : `assertServableQuery: ${where} carries the key ${JSON.stringify(unknown)}, which ` + 'the query grammar does not define; an unknown key would be silently ignored ' + 'by every engine rather than honoured, so it is refused';
146
+ };
147
+ /**
148
+ * The unknown-key half of `servableQueryFault`, on its own: the refusal of a key the
149
+ * grammar does not define on the envelope or on any item, or `undefined`.
150
+ *
151
+ * It is the same rule and not a second statement of it — `servableQueryFault` calls
152
+ * this first. It is exported because ONE caller has to tell the two halves apart:
153
+ * the codec's read ledger refuses an unservable SHAPE on its own error channel, and
154
+ * must not report an unknown key as one. An unknown key is not something a slice's
155
+ * tags can fix, so the ledger leaves it to the store, which refuses it as a defect.
156
+ */
157
+ const unknownQueryKeyFault = query => unknownKeyFault('a query', query, Query.fields) ?? query.items.reduce((fault, item) => fault ?? unknownKeyFault('a query item', item, QueryItem.fields), undefined);
106
158
  /**
107
159
  * The servable grammar as a TOTAL function: the reason `query` cannot be served,
108
160
  * or `undefined` when it can.
@@ -123,8 +175,12 @@ const assertServableQuery = query => {
123
175
  * side's own (`projectionWiringFault` returns `string | undefined` for the same
124
176
  * reason), so a fault is one exit route and one return type.
125
177
  */
126
- exports.assertServableQuery = assertServableQuery;
178
+ exports.unknownQueryKeyFault = unknownQueryKeyFault;
127
179
  const servableQueryFault = query => {
180
+ const unknownKey = unknownQueryKeyFault(query);
181
+ if (unknownKey !== undefined) {
182
+ return unknownKey;
183
+ }
128
184
  const {
129
185
  items
130
186
  } = query;
@@ -182,7 +238,7 @@ const classifyServableQuery = query => {
182
238
  }
183
239
  const only = query.items.length === 1 ? query.items[0] : undefined;
184
240
  if (only !== undefined && only.tags.length === 0) {
185
- return _effect.Array.isNonEmptyReadonlyArray(only.types) ? {
241
+ return _effect.Array.isReadonlyArrayNonEmpty(only.types) ? {
186
242
  kind: 'typeOnly',
187
243
  types: only.types
188
244
  } : {
@@ -1 +1 @@
1
- {"version":3,"file":"Query.js","names":["_effect","require","_Event","QueryItem","exports","Schema","Struct","types","Array","EventType","tags","Tag","Query","items","matchesQueryItem","event","item","typeOk","length","includes","type","eventTags","Set","every","tag","has","matchesQuery","query","some","matchesSequenced","sequenced","assertServableQuery","fault","servableQueryFault","undefined","Error","classifyServableQuery","kind","only","Arr","isNonEmptyReadonlyArray","fields","all","or"],"sources":["../../src/Query.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"Query.js","names":["_effect","require","_Event","QueryItem","exports","Schema","Struct","types","Array","EventType","tags","Tag","Query","items","matchesQueryItem","event","item","typeOk","length","includes","type","eventTags","Set","every","tag","has","matchesQuery","query","some","matchesSequenced","sequenced","assertServableQuery","fault","servableQueryFault","undefined","Error","unknownKeyFault","where","value","schemaFields","unknown","Object","keys","find","key","hasOwn","JSON","stringify","unknownQueryKeyFault","fields","reduce","unknownKey","classifyServableQuery","kind","only","Arr","isReadonlyArrayNonEmpty","all","or"],"sources":["../../src/Query.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -114,12 +114,12 @@ const DEFAULT_POLL_INTERVAL_MILLIS = exports.DEFAULT_POLL_INTERVAL_MILLIS = 1000
114
114
  * the key NAME, the VALIDATION and the DEFAULT from one place, so a caller who has
115
115
  * tuned one engine's subscribe reads another's without translating, and the two
116
116
  * cannot drift in what they ACCEPT any more than in what they assume. Two engines
117
- * hand-authoring the identical `Schema.optionalWith(…)` twice were identical only
117
+ * hand-authoring the identical `Schema.optionalKey(…)` twice were identical only
118
118
  * by inspection.
119
119
  *
120
120
  * The bounds differ between the two because the quantities do. `pollInterval` is a
121
- * duration, so a fraction of a millisecond is meaningful and only `positive` and
122
- * `finite` matter: at zero or below the loop would spin without ever waiting for a
121
+ * duration, so a fraction of a millisecond is meaningful and only `isGreaterThan(0)`
122
+ * and `isFinite` matter: at zero or below the loop would spin without ever waiting for a
123
123
  * wake, and at `Infinity` the periodic re-read — the half that carries delivery —
124
124
  * would never come, which silently converts the wake source from an optimisation
125
125
  * into the guarantee. `catchUpPageSize` is a COUNT, so it must be an integer
@@ -127,17 +127,13 @@ const DEFAULT_POLL_INTERVAL_MILLIS = exports.DEFAULT_POLL_INTERVAL_MILLIS = 1000
127
127
  * length against it to decide the tail is drained, and a page of zero would report
128
128
  * "drained" forever without reading anything.
129
129
  *
130
- * `exact: true` on both keeps the decoded type at `key?: T` rather than
130
+ * `optionalKey` on both keeps the decoded type at `key?: T` rather than
131
131
  * `T | undefined`, matching the repo's `exactOptionalPropertyTypes` discipline — an
132
132
  * absent key, never an explicit `undefined`.
133
133
  */
134
134
  const subscriptionTuningFields = exports.subscriptionTuningFields = {
135
- pollInterval: /*#__PURE__*/_effect.Schema.optionalWith(/*#__PURE__*/_effect.Schema.Number.pipe(/*#__PURE__*/_effect.Schema.positive(), /*#__PURE__*/_effect.Schema.finite()), {
136
- exact: true
137
- }),
138
- catchUpPageSize: /*#__PURE__*/_effect.Schema.optionalWith(/*#__PURE__*/_effect.Schema.Int.pipe(/*#__PURE__*/_effect.Schema.positive()), {
139
- exact: true
140
- })
135
+ pollInterval: /*#__PURE__*/_effect.Schema.optionalKey(/*#__PURE__*/_effect.Schema.Number.check(/*#__PURE__*/_effect.Schema.isGreaterThan(0), /*#__PURE__*/_effect.Schema.isFinite())),
136
+ catchUpPageSize: /*#__PURE__*/_effect.Schema.optionalKey(/*#__PURE__*/_effect.Schema.Int.check(/*#__PURE__*/_effect.Schema.isGreaterThan(0)))
141
137
  };
142
138
  /**
143
139
  * Retry a TRANSIENT poll fault (an `E` from one reconcile read) on a capped
@@ -146,16 +142,16 @@ const subscriptionTuningFields = exports.subscriptionTuningFields = {
146
142
  * past the cap propagates (the decorator dies it) — infrastructure stays off the
147
143
  * contract channel while the stream survives ordinary transients.
148
144
  */
149
- const POLL_RETRY_SCHEDULE = /*#__PURE__*/_effect.Schedule.exponential('100 millis', 2).pipe(/*#__PURE__*/_effect.Schedule.union(/*#__PURE__*/_effect.Schedule.spaced('2 seconds')), /*#__PURE__*/_effect.Schedule.intersect(/*#__PURE__*/_effect.Schedule.recurs(20)));
145
+ const POLL_RETRY_SCHEDULE = /*#__PURE__*/_effect.Schedule.max([/*#__PURE__*/_effect.Schedule.min([/*#__PURE__*/_effect.Schedule.exponential('100 millis', 2), /*#__PURE__*/_effect.Schedule.spaced('2 seconds')]), /*#__PURE__*/_effect.Schedule.recurs(20)]);
150
146
  /**
151
147
  * Build the subscription `Stream`: catch up from `after`, then track the live tail.
152
- * The cursor threads through the `unfoldChunkEffect` unfold state, starting at
153
- * `after` and advancing to the last delivered position each batch; the stream never
154
- * completes on its own (always `Option.some`), so the enclosing scope interrupts it
155
- * on close. An `E` mid-stream stays on the channel; `makeContractStore` dies it
148
+ * The cursor threads through the `Stream.paginate` state, starting at `after` and
149
+ * advancing to the last delivered position each batch; the stream never completes
150
+ * on its own (the next state is always `Option.some`), so the enclosing scope
151
+ * interrupts it on close. An `E` mid-stream stays on the channel; `makeContractStore` dies it
156
152
  * (subscribe is `E = never` on the contract).
157
153
  */
158
- const subscribeStream = (deps, after) => _effect.Stream.unwrapScoped(_effect.Effect.gen(function* () {
154
+ const subscribeStream = (deps, after) => _effect.Stream.unwrap(_effect.Effect.gen(function* () {
159
155
  // The wake primitive: a sliding(1) queue holding at most one pending
160
156
  // signal. The wake source `offer`s an opaque wake on every notification;
161
157
  // the poll loop `take`s (with a timeout). The lost-wakeup guard is
@@ -171,7 +167,7 @@ const subscribeStream = (deps, after) => _effect.Stream.unwrapScoped(_effect.Eff
171
167
  // silently-dropped (and later re-established) wake source loses no
172
168
  // positions. Absent a wake source entirely, `Stream.never` makes this a
173
169
  // fibre that parks forever and delivery falls to the poll alone.
174
- const listener = (deps.listen ?? _effect.Stream.never).pipe(_effect.Stream.tap(() => _effect.Queue.offer(wake, undefined)), _effect.Stream.retry(_effect.Schedule.exponential('50 millis').pipe(_effect.Schedule.union(_effect.Schedule.spaced('1 seconds')))));
170
+ const listener = (deps.listen ?? _effect.Stream.never).pipe(_effect.Stream.tap(() => _effect.Queue.offer(wake, undefined)), _effect.Stream.retry(_effect.Schedule.min([_effect.Schedule.exponential('50 millis'), _effect.Schedule.spaced('1 seconds')])));
175
171
  // Drain the wake source into the wake queue on a background fibre; the
176
172
  // scope releases it. We never read its elements directly — every
177
173
  // notification is an opaque wake signal.
@@ -203,7 +199,7 @@ const subscribeStream = (deps, after) => _effect.Stream.unwrapScoped(_effect.Eff
203
199
  }).pipe(_effect.Effect.retry(POLL_RETRY_SCHEDULE));
204
200
  /**
205
201
  * Produce the NEXT non-empty batch from a cursor position, together with
206
- * the advanced cursor — a pure `Position → Effect<[Chunk, next]>`. The
202
+ * the advanced cursor — a pure `Position → Effect<[events, next]>`. The
207
203
  * `> boundary` de-dup is enforced by `fetch`'s exclusive lower bound, so a
208
204
  * live re-read after a wake never re-emits an already-delivered position.
209
205
  *
@@ -211,25 +207,27 @@ const subscribeStream = (deps, after) => _effect.Stream.unwrapScoped(_effect.Eff
211
207
  * the cursor. If a drain yields nothing, wait for a wake but only up to the
212
208
  * poll interval, then loop and re-read ANYWAY — the periodic position-based
213
209
  * poll that makes delivery correct independently of any wake. This effect
214
- * only completes by returning a non-empty chunk; the scope interrupts it on
210
+ * only completes by returning a non-empty batch; the scope interrupts it on
215
211
  * close.
216
212
  */
217
213
  const nextBatch = from => _effect.Effect.gen(function* () {
218
214
  while (true) {
219
- yield* _effect.Queue.takeAll(wake);
215
+ // `Queue.clear` and not `Queue.takeAll`: the drain must NOT wait, and
216
+ // `takeAll` suspends until at least one signal is present.
217
+ yield* _effect.Queue.clear(wake);
220
218
  const {
221
219
  events,
222
220
  last
223
221
  } = yield* drainFrom(from);
224
222
  if (events.length > 0) {
225
- return [_effect.Chunk.fromIterable(events), last];
223
+ return [events, last];
226
224
  }
227
225
  // Nothing new: block on a wake, but only up to the poll interval — on
228
226
  // timeout we fall through and the loop re-reads regardless.
229
227
  yield* _effect.Effect.ignore(_effect.Effect.timeout(_effect.Queue.take(wake), deps.pollInterval));
230
228
  }
231
229
  });
232
- return _effect.Stream.unfoldChunkEffect(after ?? _Event.ORIGIN, from => _effect.Effect.map(nextBatch(from), _effect.Option.some));
230
+ return _effect.Stream.paginate(after ?? _Event.ORIGIN, from => _effect.Effect.map(nextBatch(from), ([events, last]) => [events, _effect.Option.some(last)]));
233
231
  }));
234
232
  exports.subscribeStream = subscribeStream;
235
233
  //# sourceMappingURL=SubscribeMachine.js.map