@invinite-org/chartlang-runtime 1.1.1 → 1.2.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.
Files changed (226) hide show
  1. package/CHANGELOG.md +236 -0
  2. package/dist/barPoint.d.ts +20 -0
  3. package/dist/barPoint.d.ts.map +1 -0
  4. package/dist/barPoint.js +72 -0
  5. package/dist/barPoint.js.map +1 -0
  6. package/dist/bufferSnapshot.d.ts +102 -0
  7. package/dist/bufferSnapshot.d.ts.map +1 -0
  8. package/dist/bufferSnapshot.js +119 -0
  9. package/dist/bufferSnapshot.js.map +1 -0
  10. package/dist/createScriptRunner.d.ts +6 -3
  11. package/dist/createScriptRunner.d.ts.map +1 -1
  12. package/dist/createScriptRunner.js +29 -5
  13. package/dist/createScriptRunner.js.map +1 -1
  14. package/dist/dep/DepRunner.d.ts.map +1 -1
  15. package/dist/dep/DepRunner.js +1 -0
  16. package/dist/dep/DepRunner.js.map +1 -1
  17. package/dist/emit/draw/boxes/fillBetween.d.ts +45 -0
  18. package/dist/emit/draw/boxes/fillBetween.d.ts.map +1 -0
  19. package/dist/emit/draw/boxes/fillBetween.js +36 -0
  20. package/dist/emit/draw/boxes/fillBetween.js.map +1 -0
  21. package/dist/emit/draw/handle.d.ts +9 -0
  22. package/dist/emit/draw/handle.d.ts.map +1 -1
  23. package/dist/emit/draw/handle.js +65 -10
  24. package/dist/emit/draw/handle.js.map +1 -1
  25. package/dist/emit/draw/namespace.d.ts +4 -3
  26. package/dist/emit/draw/namespace.d.ts.map +1 -1
  27. package/dist/emit/draw/namespace.js +6 -3
  28. package/dist/emit/draw/namespace.js.map +1 -1
  29. package/dist/emit/plot.d.ts +7 -0
  30. package/dist/emit/plot.d.ts.map +1 -1
  31. package/dist/emit/plot.js +13 -0
  32. package/dist/emit/plot.js.map +1 -1
  33. package/dist/execution/dispose.d.ts.map +1 -1
  34. package/dist/execution/dispose.js +16 -0
  35. package/dist/execution/dispose.js.map +1 -1
  36. package/dist/execution/runComputeStep.d.ts.map +1 -1
  37. package/dist/execution/runComputeStep.js +10 -1
  38. package/dist/execution/runComputeStep.js.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/persistentStateStore.runtime.d.ts.map +1 -1
  44. package/dist/persistentStateStore.runtime.js +21 -7
  45. package/dist/persistentStateStore.runtime.js.map +1 -1
  46. package/dist/request/index.d.ts +2 -1
  47. package/dist/request/index.d.ts.map +1 -1
  48. package/dist/request/index.js +2 -1
  49. package/dist/request/index.js.map +1 -1
  50. package/dist/request/requestNamespace.d.ts.map +1 -1
  51. package/dist/request/requestNamespace.js +16 -3
  52. package/dist/request/requestNamespace.js.map +1 -1
  53. package/dist/request/security.d.ts +20 -1
  54. package/dist/request/security.d.ts.map +1 -1
  55. package/dist/request/security.js +62 -23
  56. package/dist/request/security.js.map +1 -1
  57. package/dist/request/securityExprRunner.d.ts +133 -0
  58. package/dist/request/securityExprRunner.d.ts.map +1 -0
  59. package/dist/request/securityExprRunner.js +235 -0
  60. package/dist/request/securityExprRunner.js.map +1 -0
  61. package/dist/request/streamBars.d.ts +14 -1
  62. package/dist/request/streamBars.d.ts.map +1 -1
  63. package/dist/request/streamBars.js +39 -1
  64. package/dist/request/streamBars.js.map +1 -1
  65. package/dist/runtimeContext.d.ts +48 -0
  66. package/dist/runtimeContext.d.ts.map +1 -1
  67. package/dist/runtimeContext.js.map +1 -1
  68. package/dist/seriesView.d.ts +42 -17
  69. package/dist/seriesView.d.ts.map +1 -1
  70. package/dist/seriesView.js +65 -42
  71. package/dist/seriesView.js.map +1 -1
  72. package/dist/state/index.d.ts +2 -1
  73. package/dist/state/index.d.ts.map +1 -1
  74. package/dist/state/index.js +2 -1
  75. package/dist/state/index.js.map +1 -1
  76. package/dist/state/lifecycle.d.ts +40 -0
  77. package/dist/state/lifecycle.d.ts.map +1 -1
  78. package/dist/state/lifecycle.js +53 -0
  79. package/dist/state/lifecycle.js.map +1 -1
  80. package/dist/state/seriesPersistence.d.ts +48 -0
  81. package/dist/state/seriesPersistence.d.ts.map +1 -0
  82. package/dist/state/seriesPersistence.js +87 -0
  83. package/dist/state/seriesPersistence.js.map +1 -0
  84. package/dist/state/seriesSlot.d.ts +105 -0
  85. package/dist/state/seriesSlot.d.ts.map +1 -0
  86. package/dist/state/seriesSlot.js +123 -0
  87. package/dist/state/seriesSlot.js.map +1 -0
  88. package/dist/state/stateNamespace.d.ts.map +1 -1
  89. package/dist/state/stateNamespace.js +28 -0
  90. package/dist/state/stateNamespace.js.map +1 -1
  91. package/dist/streamState.d.ts +25 -19
  92. package/dist/streamState.d.ts.map +1 -1
  93. package/dist/streamState.js +40 -66
  94. package/dist/streamState.js.map +1 -1
  95. package/dist/ta/adx.d.ts +3 -2
  96. package/dist/ta/adx.d.ts.map +1 -1
  97. package/dist/ta/adx.js +3 -2
  98. package/dist/ta/adx.js.map +1 -1
  99. package/dist/ta/alma.d.ts +6 -4
  100. package/dist/ta/alma.d.ts.map +1 -1
  101. package/dist/ta/alma.js +19 -6
  102. package/dist/ta/alma.js.map +1 -1
  103. package/dist/ta/atr.d.ts +3 -2
  104. package/dist/ta/atr.d.ts.map +1 -1
  105. package/dist/ta/atr.js +3 -2
  106. package/dist/ta/atr.js.map +1 -1
  107. package/dist/ta/bb.d.ts +3 -2
  108. package/dist/ta/bb.d.ts.map +1 -1
  109. package/dist/ta/bb.js +3 -2
  110. package/dist/ta/bb.js.map +1 -1
  111. package/dist/ta/chaikinOsc.d.ts +3 -2
  112. package/dist/ta/chaikinOsc.d.ts.map +1 -1
  113. package/dist/ta/chaikinOsc.js +3 -2
  114. package/dist/ta/chaikinOsc.js.map +1 -1
  115. package/dist/ta/crossover.d.ts +3 -2
  116. package/dist/ta/crossover.d.ts.map +1 -1
  117. package/dist/ta/crossover.js +3 -2
  118. package/dist/ta/crossover.js.map +1 -1
  119. package/dist/ta/crossunder.d.ts +3 -2
  120. package/dist/ta/crossunder.d.ts.map +1 -1
  121. package/dist/ta/crossunder.js +3 -2
  122. package/dist/ta/crossunder.js.map +1 -1
  123. package/dist/ta/dmi.d.ts +4 -3
  124. package/dist/ta/dmi.d.ts.map +1 -1
  125. package/dist/ta/dmi.js +4 -3
  126. package/dist/ta/dmi.js.map +1 -1
  127. package/dist/ta/ema.d.ts +3 -2
  128. package/dist/ta/ema.d.ts.map +1 -1
  129. package/dist/ta/ema.js +3 -2
  130. package/dist/ta/ema.js.map +1 -1
  131. package/dist/ta/eom.d.ts +3 -1
  132. package/dist/ta/eom.d.ts.map +1 -1
  133. package/dist/ta/eom.js +3 -1
  134. package/dist/ta/eom.js.map +1 -1
  135. package/dist/ta/highestbars.d.ts +25 -0
  136. package/dist/ta/highestbars.d.ts.map +1 -0
  137. package/dist/ta/highestbars.js +106 -0
  138. package/dist/ta/highestbars.js.map +1 -0
  139. package/dist/ta/historicalVolatility.d.ts +3 -2
  140. package/dist/ta/historicalVolatility.d.ts.map +1 -1
  141. package/dist/ta/historicalVolatility.js +3 -2
  142. package/dist/ta/historicalVolatility.js.map +1 -1
  143. package/dist/ta/ichimoku.d.ts +3 -1
  144. package/dist/ta/ichimoku.d.ts.map +1 -1
  145. package/dist/ta/ichimoku.js +3 -1
  146. package/dist/ta/ichimoku.js.map +1 -1
  147. package/dist/ta/lowestbars.d.ts +25 -0
  148. package/dist/ta/lowestbars.d.ts.map +1 -0
  149. package/dist/ta/lowestbars.js +102 -0
  150. package/dist/ta/lowestbars.js.map +1 -0
  151. package/dist/ta/macd.d.ts +3 -2
  152. package/dist/ta/macd.d.ts.map +1 -1
  153. package/dist/ta/macd.js +3 -2
  154. package/dist/ta/macd.js.map +1 -1
  155. package/dist/ta/massIndex.d.ts +3 -2
  156. package/dist/ta/massIndex.d.ts.map +1 -1
  157. package/dist/ta/massIndex.js +3 -2
  158. package/dist/ta/massIndex.js.map +1 -1
  159. package/dist/ta/mfi.d.ts +3 -1
  160. package/dist/ta/mfi.d.ts.map +1 -1
  161. package/dist/ta/mfi.js +3 -1
  162. package/dist/ta/mfi.js.map +1 -1
  163. package/dist/ta/netVolume.d.ts +3 -1
  164. package/dist/ta/netVolume.d.ts.map +1 -1
  165. package/dist/ta/netVolume.js +3 -1
  166. package/dist/ta/netVolume.js.map +1 -1
  167. package/dist/ta/nvi.d.ts +3 -1
  168. package/dist/ta/nvi.d.ts.map +1 -1
  169. package/dist/ta/nvi.js +3 -1
  170. package/dist/ta/nvi.js.map +1 -1
  171. package/dist/ta/persistence.d.ts.map +1 -1
  172. package/dist/ta/persistence.js +1 -40
  173. package/dist/ta/persistence.js.map +1 -1
  174. package/dist/ta/ppo.d.ts +3 -2
  175. package/dist/ta/ppo.d.ts.map +1 -1
  176. package/dist/ta/ppo.js +3 -2
  177. package/dist/ta/ppo.js.map +1 -1
  178. package/dist/ta/pvi.d.ts +3 -1
  179. package/dist/ta/pvi.d.ts.map +1 -1
  180. package/dist/ta/pvi.js +3 -1
  181. package/dist/ta/pvi.js.map +1 -1
  182. package/dist/ta/pvo.d.ts +3 -1
  183. package/dist/ta/pvo.d.ts.map +1 -1
  184. package/dist/ta/pvo.js +3 -1
  185. package/dist/ta/pvo.js.map +1 -1
  186. package/dist/ta/pvt.d.ts +3 -1
  187. package/dist/ta/pvt.d.ts.map +1 -1
  188. package/dist/ta/pvt.js +3 -1
  189. package/dist/ta/pvt.js.map +1 -1
  190. package/dist/ta/registry.d.ts +7 -1
  191. package/dist/ta/registry.d.ts.map +1 -1
  192. package/dist/ta/registry.js +4 -0
  193. package/dist/ta/registry.js.map +1 -1
  194. package/dist/ta/rsi.d.ts +3 -2
  195. package/dist/ta/rsi.d.ts.map +1 -1
  196. package/dist/ta/rsi.js +3 -2
  197. package/dist/ta/rsi.js.map +1 -1
  198. package/dist/ta/rvi.d.ts +3 -2
  199. package/dist/ta/rvi.d.ts.map +1 -1
  200. package/dist/ta/rvi.js +3 -2
  201. package/dist/ta/rvi.js.map +1 -1
  202. package/dist/ta/sma.d.ts +6 -3
  203. package/dist/ta/sma.d.ts.map +1 -1
  204. package/dist/ta/sma.js +6 -3
  205. package/dist/ta/sma.js.map +1 -1
  206. package/dist/ta/stdev.d.ts +3 -2
  207. package/dist/ta/stdev.d.ts.map +1 -1
  208. package/dist/ta/stdev.js +3 -2
  209. package/dist/ta/stdev.js.map +1 -1
  210. package/dist/ta/trendStrengthIndex.d.ts +3 -2
  211. package/dist/ta/trendStrengthIndex.d.ts.map +1 -1
  212. package/dist/ta/trendStrengthIndex.js +3 -2
  213. package/dist/ta/trendStrengthIndex.js.map +1 -1
  214. package/dist/ta/trix.d.ts +4 -3
  215. package/dist/ta/trix.d.ts.map +1 -1
  216. package/dist/ta/trix.js +4 -3
  217. package/dist/ta/trix.js.map +1 -1
  218. package/dist/ta/vortex.d.ts +3 -2
  219. package/dist/ta/vortex.d.ts.map +1 -1
  220. package/dist/ta/vortex.js +3 -2
  221. package/dist/ta/vortex.js.map +1 -1
  222. package/package.json +3 -3
  223. package/dist/ta/lib/applyOffset.d.ts +0 -19
  224. package/dist/ta/lib/applyOffset.d.ts.map +0 -1
  225. package/dist/ta/lib/applyOffset.js +0 -38
  226. package/dist/ta/lib/applyOffset.js.map +0 -1
@@ -10,6 +10,14 @@ import type { RingBufferLike } from "./ringBuffer.js";
10
10
  * Property reads dispatch as follows:
11
11
  * - `series.current` → `buf.at(0)`
12
12
  * - `series.length` → `buf.length`
13
+ * - `series.valueOf` / `series[Symbol.toPrimitive]` → a function returning
14
+ * `buf.at(0)`, so a numeric series coerces to its **current** value in any
15
+ * value context (`series * 2`, `series > x`, `` `${series}` ``,
16
+ * `Math.max(series, …)`). This is what lets `bar.close` be used both as a
17
+ * scalar and indexed as a series. Coercion is harmless for non-numeric
18
+ * buffers — nothing coerces those. Note `Number.isFinite(series)` is still
19
+ * `false` (it does not coerce) and `series === 42` is `false` (object vs
20
+ * number); use `series.current` / `+series` there.
13
21
  * - `series[n]` (string coerces to a non-negative integer) → `buf.at(n)`
14
22
  * - any other key → `undefined`
15
23
  *
@@ -30,30 +38,47 @@ import type { RingBufferLike } from "./ringBuffer.js";
30
38
  */
31
39
  export declare function makeSeriesView<T>(buf: RingBufferLike<T>): Series<T>;
32
40
  /**
33
- * Offset-shifted variant of {@link makeSeriesView}. `offset === 0`
34
- * returns the same Proxy shape (and is the identity-preserving fast
35
- * path callers should special-case at the call site). For
36
- * `offset === k > 0`, `view.current === buf.at(k)` i.e. the value
37
- * `k` bars ago, matching `lib/applyOffset`'s
38
- * `out[i] = values[i offset]` semantics. For `offset === -k`,
39
- * `view.current === buf.at(-k)` an OOR read returning the underlying
40
- * sentinel (NaN for `Float64RingBuffer`, `undefined` for object
41
- * `RingBuffer`).
41
+ * Offset-tagging variant of {@link makeSeriesView}. It returns the
42
+ * **unshifted** view (delegating to {@link makeSeriesView}) and, for a
43
+ * non-zero `offset`, records `view offset` in a module-level
44
+ * `WeakMap` side-table read by `plot()` via {@link seriesOffsetOf}. The
45
+ * offset is **presentation-only** — `view.current` is `buf.at(0)`, not a
46
+ * value `offset` bars ago so both shift directions are expressible and
47
+ * alerts / `state.*` see the unshifted value. `offset === 0` records
48
+ * nothing (byte-identical to a plain {@link makeSeriesView}).
42
49
  *
43
- * The shift is applied on every read no allocation, no per-bar
44
- * work. Callers cache the returned Proxy per `(slot, offset)` pair so
45
- * the view's identity stays stable across bars.
50
+ * Callers cache the returned view per `(slot, offset)` pair so the
51
+ * view's identity and therefore its recorded offset stays stable
52
+ * across bars.
46
53
  *
47
54
  * @since 0.2
55
+ * @stable
48
56
  * @example
49
- * // import { Float64RingBuffer, makeShiftedSeriesView }
57
+ * // import { Float64RingBuffer, makeShiftedSeriesView, seriesOffsetOf }
50
58
  * // from "@invinite-org/chartlang-runtime";
51
59
  * // const buf = new Float64RingBuffer(8);
52
60
  * // buf.append(10); buf.append(20); buf.append(30);
53
- * // const view = makeShiftedSeriesView<number>(buf, 1);
54
- * // view.current; // 20 (one bar ago)
55
- * // view[0]; // 20
56
- * // view[1]; // 10
61
+ * // const view = makeShiftedSeriesView<number>(buf, 5);
62
+ * // view.current; // 30 (unshifted the offset does NOT lag the read)
63
+ * // seriesOffsetOf(view); // 5 (presentation x-shift carried to the emission)
57
64
  */
58
65
  export declare function makeShiftedSeriesView<T>(buf: RingBufferLike<T>, offset: number): Series<T>;
66
+ /**
67
+ * Read the presentation x-shift recorded for `series` by
68
+ * {@link makeShiftedSeriesView}, or `0` when the series is untagged (a
69
+ * plain {@link makeSeriesView} view, an `offset === 0` view, or any
70
+ * non-runtime Series). `plot()` calls this to populate
71
+ * `PlotEmission.xShift`; a `0` result omits the field so a no-offset
72
+ * plot stays byte-identical to today.
73
+ *
74
+ * @since 0.2
75
+ * @stable
76
+ * @example
77
+ * // import { Float64RingBuffer, makeShiftedSeriesView, seriesOffsetOf }
78
+ * // from "@invinite-org/chartlang-runtime";
79
+ * // const buf = new Float64RingBuffer(8);
80
+ * // const shifted = makeShiftedSeriesView<number>(buf, -3);
81
+ * // seriesOffsetOf(shifted); // -3
82
+ */
83
+ export declare function seriesOffsetOf(series: Series<unknown>): number;
59
84
  //# sourceMappingURL=seriesView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"seriesView.d.ts","sourceRoot":"","sources":["../src/seriesView.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAoBnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAqB1F"}
1
+ {"version":3,"file":"seriesView.d.ts","sourceRoot":"","sources":["../src/seriesView.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAuBnE;AAcD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAI1F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAE9D"}
@@ -10,6 +10,14 @@
10
10
  * Property reads dispatch as follows:
11
11
  * - `series.current` → `buf.at(0)`
12
12
  * - `series.length` → `buf.length`
13
+ * - `series.valueOf` / `series[Symbol.toPrimitive]` → a function returning
14
+ * `buf.at(0)`, so a numeric series coerces to its **current** value in any
15
+ * value context (`series * 2`, `series > x`, `` `${series}` ``,
16
+ * `Math.max(series, …)`). This is what lets `bar.close` be used both as a
17
+ * scalar and indexed as a series. Coercion is harmless for non-numeric
18
+ * buffers — nothing coerces those. Note `Number.isFinite(series)` is still
19
+ * `false` (it does not coerce) and `series === 42` is `false` (object vs
20
+ * number); use `series.current` / `+series` there.
13
21
  * - `series[n]` (string coerces to a non-negative integer) → `buf.at(n)`
14
22
  * - any other key → `undefined`
15
23
  *
@@ -35,6 +43,10 @@ export function makeSeriesView(buf) {
35
43
  return buf.at(0);
36
44
  if (prop === "length")
37
45
  return buf.length;
46
+ if (prop === "valueOf")
47
+ return () => buf.at(0);
48
+ if (prop === Symbol.toPrimitive)
49
+ return () => buf.at(0);
38
50
  if (typeof prop === "string") {
39
51
  const n = Number(prop);
40
52
  if (Number.isInteger(n) && n >= 0)
@@ -45,6 +57,8 @@ export function makeSeriesView(buf) {
45
57
  has(_target, prop) {
46
58
  if (prop === "current" || prop === "length")
47
59
  return true;
60
+ if (prop === "valueOf" || prop === Symbol.toPrimitive)
61
+ return true;
48
62
  if (typeof prop === "string") {
49
63
  const n = Number(prop);
50
64
  return Number.isInteger(n) && n >= 0;
@@ -54,56 +68,65 @@ export function makeSeriesView(buf) {
54
68
  });
55
69
  }
56
70
  /**
57
- * Offset-shifted variant of {@link makeSeriesView}. `offset === 0`
58
- * returns the same Proxy shape (and is the identity-preserving fast
59
- * path callers should special-case at the call site). For
60
- * `offset === k > 0`, `view.current === buf.at(k)` i.e. the value
61
- * `k` bars ago, matching `lib/applyOffset`'s
62
- * `out[i] = values[i offset]` semantics. For `offset === -k`,
63
- * `view.current === buf.at(-k)` an OOR read returning the underlying
64
- * sentinel (NaN for `Float64RingBuffer`, `undefined` for object
65
- * `RingBuffer`).
71
+ * Module-level side-table recording the **presentation x-shift** declared
72
+ * for an offset-tagged Series view. `offset` is no longer a value-read
73
+ * transform (Option A, bidirectional-plot-offset): the series value is
74
+ * always the unshifted `buf.at(0)`; the recorded offset rides the plot
75
+ * emission as `PlotEmission.xShift` so the adapter renders the series
76
+ * shifted (`+n` right / future, `−n` left / past) without changing the
77
+ * numbers alerts and `state.*` see. Keyed weakly on the view object so a
78
+ * dropped slot's tag is collected with it.
79
+ */
80
+ const seriesOffsets = new WeakMap();
81
+ /**
82
+ * Offset-tagging variant of {@link makeSeriesView}. It returns the
83
+ * **unshifted** view (delegating to {@link makeSeriesView}) and, for a
84
+ * non-zero `offset`, records `view → offset` in a module-level
85
+ * `WeakMap` side-table read by `plot()` via {@link seriesOffsetOf}. The
86
+ * offset is **presentation-only** — `view.current` is `buf.at(0)`, not a
87
+ * value `offset` bars ago — so both shift directions are expressible and
88
+ * alerts / `state.*` see the unshifted value. `offset === 0` records
89
+ * nothing (byte-identical to a plain {@link makeSeriesView}).
66
90
  *
67
- * The shift is applied on every read no allocation, no per-bar
68
- * work. Callers cache the returned Proxy per `(slot, offset)` pair so
69
- * the view's identity stays stable across bars.
91
+ * Callers cache the returned view per `(slot, offset)` pair so the
92
+ * view's identity and therefore its recorded offset stays stable
93
+ * across bars.
70
94
  *
71
95
  * @since 0.2
96
+ * @stable
72
97
  * @example
73
- * // import { Float64RingBuffer, makeShiftedSeriesView }
98
+ * // import { Float64RingBuffer, makeShiftedSeriesView, seriesOffsetOf }
74
99
  * // from "@invinite-org/chartlang-runtime";
75
100
  * // const buf = new Float64RingBuffer(8);
76
101
  * // buf.append(10); buf.append(20); buf.append(30);
77
- * // const view = makeShiftedSeriesView<number>(buf, 1);
78
- * // view.current; // 20 (one bar ago)
79
- * // view[0]; // 20
80
- * // view[1]; // 10
102
+ * // const view = makeShiftedSeriesView<number>(buf, 5);
103
+ * // view.current; // 30 (unshifted the offset does NOT lag the read)
104
+ * // seriesOffsetOf(view); // 5 (presentation x-shift carried to the emission)
81
105
  */
82
106
  export function makeShiftedSeriesView(buf, offset) {
83
- if (offset === 0)
84
- return makeSeriesView(buf);
85
- return new Proxy({}, {
86
- get(_target, prop) {
87
- if (prop === "current")
88
- return buf.at(offset);
89
- if (prop === "length")
90
- return buf.length;
91
- if (typeof prop === "string") {
92
- const n = Number(prop);
93
- if (Number.isInteger(n) && n >= 0)
94
- return buf.at(n + offset);
95
- }
96
- return undefined;
97
- },
98
- has(_target, prop) {
99
- if (prop === "current" || prop === "length")
100
- return true;
101
- if (typeof prop === "string") {
102
- const n = Number(prop);
103
- return Number.isInteger(n) && n >= 0;
104
- }
105
- return false;
106
- },
107
- });
107
+ const view = makeSeriesView(buf);
108
+ if (offset !== 0)
109
+ seriesOffsets.set(view, offset);
110
+ return view;
111
+ }
112
+ /**
113
+ * Read the presentation x-shift recorded for `series` by
114
+ * {@link makeShiftedSeriesView}, or `0` when the series is untagged (a
115
+ * plain {@link makeSeriesView} view, an `offset === 0` view, or any
116
+ * non-runtime Series). `plot()` calls this to populate
117
+ * `PlotEmission.xShift`; a `0` result omits the field so a no-offset
118
+ * plot stays byte-identical to today.
119
+ *
120
+ * @since 0.2
121
+ * @stable
122
+ * @example
123
+ * // import { Float64RingBuffer, makeShiftedSeriesView, seriesOffsetOf }
124
+ * // from "@invinite-org/chartlang-runtime";
125
+ * // const buf = new Float64RingBuffer(8);
126
+ * // const shifted = makeShiftedSeriesView<number>(buf, -3);
127
+ * // seriesOffsetOf(shifted); // -3
128
+ */
129
+ export function seriesOffsetOf(series) {
130
+ return seriesOffsets.get(series) ?? 0;
108
131
  }
109
132
  //# sourceMappingURL=seriesView.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"seriesView.js","sourceRoot":"","sources":["../src/seriesView.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAM/D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,cAAc,CAAI,GAAsB;IACpD,OAAO,IAAI,KAAK,CAAC,EAAe,EAAE;QAC9B,GAAG,CAAC,OAAO,EAAE,IAAI;YACb,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,GAAG,CAAC,MAAM,CAAC;YACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACxD,CAAC;YACD,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,IAAI;YACb,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACzD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;KACJ,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,qBAAqB,CAAI,GAAsB,EAAE,MAAc;IAC3E,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAI,GAAG,CAAC,CAAC;IAChD,OAAO,IAAI,KAAK,CAAC,EAAe,EAAE;QAC9B,GAAG,CAAC,OAAO,EAAE,IAAI;YACb,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAC9C,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,GAAG,CAAC,MAAM,CAAC;YACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;YACjE,CAAC;YACD,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,IAAI;YACb,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACzD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;KACJ,CAAC,CAAC;AACP,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { Series } from \"@invinite-org/chartlang-core\";\n\nimport type { RingBufferLike } from \"./ringBuffer.js\";\n\n/**\n * Wrap a `RingBufferLike<T>` in the user-facing `Series<T>` Proxy shape\n *. The Proxy is created **once per backing buffer**\n * at stream/slot construction time and re-used across every bar — its\n * identity is stable so script authors can keep `const ema = ta.ema(...)`\n * at the top of `compute` and reference `ema` the same way every bar.\n *\n * Property reads dispatch as follows:\n * - `series.current` → `buf.at(0)`\n * - `series.length` → `buf.length`\n * - `series[n]` (string coerces to a non-negative integer) → `buf.at(n)`\n * - any other key → `undefined`\n *\n * For numeric buffers (`Float64RingBuffer`) out-of-range reads return\n * `NaN`; for object buffers they return `undefined`. The Proxy passes\n * the underlying sentinel through verbatim.\n *\n * @since 0.1\n * @example\n * // import { Float64RingBuffer, makeSeriesView }\n * // from \"@invinite-org/chartlang-runtime\";\n * // const buf = new Float64RingBuffer(8);\n * // const view = makeSeriesView<number>(buf);\n * // buf.append(42);\n * // view.current; // 42\n * // view[0]; // 42\n * // view.length; // 1\n */\nexport function makeSeriesView<T>(buf: RingBufferLike<T>): Series<T> {\n return new Proxy({} as Series<T>, {\n get(_target, prop) {\n if (prop === \"current\") return buf.at(0);\n if (prop === \"length\") return buf.length;\n if (typeof prop === \"string\") {\n const n = Number(prop);\n if (Number.isInteger(n) && n >= 0) return buf.at(n);\n }\n return undefined;\n },\n has(_target, prop) {\n if (prop === \"current\" || prop === \"length\") return true;\n if (typeof prop === \"string\") {\n const n = Number(prop);\n return Number.isInteger(n) && n >= 0;\n }\n return false;\n },\n });\n}\n\n/**\n * Offset-shifted variant of {@link makeSeriesView}. `offset === 0`\n * returns the same Proxy shape (and is the identity-preserving fast\n * path callers should special-case at the call site). For\n * `offset === k > 0`, `view.current === buf.at(k)` i.e. the value\n * `k` bars ago, matching `lib/applyOffset`'s\n * `out[i] = values[i offset]` semantics. For `offset === -k`,\n * `view.current === buf.at(-k)` an OOR read returning the underlying\n * sentinel (NaN for `Float64RingBuffer`, `undefined` for object\n * `RingBuffer`).\n *\n * The shift is applied on every readno allocation, no per-bar\n * work. Callers cache the returned Proxy per `(slot, offset)` pair so\n * the view's identity stays stable across bars.\n *\n * @since 0.2\n * @example\n * // import { Float64RingBuffer, makeShiftedSeriesView }\n * // from \"@invinite-org/chartlang-runtime\";\n * // const buf = new Float64RingBuffer(8);\n * // buf.append(10); buf.append(20); buf.append(30);\n * // const view = makeShiftedSeriesView<number>(buf, 1);\n * // view.current; // 20 (one bar ago)\n * // view[0]; // 20\n * // view[1]; // 10\n */\nexport function makeShiftedSeriesView<T>(buf: RingBufferLike<T>, offset: number): Series<T> {\n if (offset === 0) return makeSeriesView<T>(buf);\n return new Proxy({} as Series<T>, {\n get(_target, prop) {\n if (prop === \"current\") return buf.at(offset);\n if (prop === \"length\") return buf.length;\n if (typeof prop === \"string\") {\n const n = Number(prop);\n if (Number.isInteger(n) && n >= 0) return buf.at(n + offset);\n }\n return undefined;\n },\n has(_target, prop) {\n if (prop === \"current\" || prop === \"length\") return true;\n if (typeof prop === \"string\") {\n const n = Number(prop);\n return Number.isInteger(n) && n >= 0;\n }\n return false;\n },\n });\n}\n"]}
1
+ {"version":3,"file":"seriesView.js","sourceRoot":"","sources":["../src/seriesView.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAM/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,cAAc,CAAI,GAAsB;IACpD,OAAO,IAAI,KAAK,CAAC,EAAe,EAAE;QAC9B,GAAG,CAAC,OAAO,EAAE,IAAI;YACb,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,GAAG,CAAC,MAAM,CAAC;YACzC,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/C,IAAI,IAAI,KAAK,MAAM,CAAC,WAAW;gBAAE,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACxD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACxD,CAAC;YACD,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,IAAI;YACb,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACzD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,CAAC,WAAW;gBAAE,OAAO,IAAI,CAAC;YACnE,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;KACJ,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,aAAa,GAAG,IAAI,OAAO,EAA2B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,qBAAqB,CAAI,GAAsB,EAAE,MAAc;IAC3E,MAAM,IAAI,GAAG,cAAc,CAAI,GAAG,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,CAAC;QAAE,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,MAAuB;IAClD,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { Series } from \"@invinite-org/chartlang-core\";\n\nimport type { RingBufferLike } from \"./ringBuffer.js\";\n\n/**\n * Wrap a `RingBufferLike<T>` in the user-facing `Series<T>` Proxy shape\n *. The Proxy is created **once per backing buffer**\n * at stream/slot construction time and re-used across every bar — its\n * identity is stable so script authors can keep `const ema = ta.ema(...)`\n * at the top of `compute` and reference `ema` the same way every bar.\n *\n * Property reads dispatch as follows:\n * - `series.current` → `buf.at(0)`\n * - `series.length` → `buf.length`\n * - `series.valueOf` / `series[Symbol.toPrimitive]` → a function returning\n * `buf.at(0)`, so a numeric series coerces to its **current** value in any\n * value context (`series * 2`, `series > x`, `` `${series}` ``,\n * `Math.max(series, …)`). This is what lets `bar.close` be used both as a\n * scalar and indexed as a series. Coercion is harmless for non-numeric\n * buffers — nothing coerces those. Note `Number.isFinite(series)` is still\n * `false` (it does not coerce) and `series === 42` is `false` (object vs\n * number); use `series.current` / `+series` there.\n * - `series[n]` (string coerces to a non-negative integer) → `buf.at(n)`\n * - any other key → `undefined`\n *\n * For numeric buffers (`Float64RingBuffer`) out-of-range reads return\n * `NaN`; for object buffers they return `undefined`. The Proxy passes\n * the underlying sentinel through verbatim.\n *\n * @since 0.1\n * @example\n * // import { Float64RingBuffer, makeSeriesView }\n * // from \"@invinite-org/chartlang-runtime\";\n * // const buf = new Float64RingBuffer(8);\n * // const view = makeSeriesView<number>(buf);\n * // buf.append(42);\n * // view.current; // 42\n * // view[0]; // 42\n * // view.length; // 1\n */\nexport function makeSeriesView<T>(buf: RingBufferLike<T>): Series<T> {\n return new Proxy({} as Series<T>, {\n get(_target, prop) {\n if (prop === \"current\") return buf.at(0);\n if (prop === \"length\") return buf.length;\n if (prop === \"valueOf\") return () => buf.at(0);\n if (prop === Symbol.toPrimitive) return () => buf.at(0);\n if (typeof prop === \"string\") {\n const n = Number(prop);\n if (Number.isInteger(n) && n >= 0) return buf.at(n);\n }\n return undefined;\n },\n has(_target, prop) {\n if (prop === \"current\" || prop === \"length\") return true;\n if (prop === \"valueOf\" || prop === Symbol.toPrimitive) return true;\n if (typeof prop === \"string\") {\n const n = Number(prop);\n return Number.isInteger(n) && n >= 0;\n }\n return false;\n },\n });\n}\n\n/**\n * Module-level side-table recording the **presentation x-shift** declared\n * for an offset-tagged Series view. `offset` is no longer a value-read\n * transform (Option A, bidirectional-plot-offset): the series value is\n * always the unshifted `buf.at(0)`; the recorded offset rides the plot\n * emission as `PlotEmission.xShift` so the adapter renders the series\n * shifted (`+n` right / future, `−n` left / past) without changing the\n * numbers alerts and `state.*` see. Keyed weakly on the view object so a\n * dropped slot's tag is collected with it.\n */\nconst seriesOffsets = new WeakMap<Series<unknown>, number>();\n\n/**\n * Offset-tagging variant of {@link makeSeriesView}. It returns the\n * **unshifted** view (delegating to {@link makeSeriesView}) and, for a\n * non-zero `offset`, records `view → offset` in a module-level\n * `WeakMap` side-table read by `plot()` via {@link seriesOffsetOf}. The\n * offset is **presentation-only** — `view.current` is `buf.at(0)`, not a\n * value `offset` bars ago so both shift directions are expressible and\n * alerts / `state.*` see the unshifted value. `offset === 0` records\n * nothing (byte-identical to a plain {@link makeSeriesView}).\n *\n * Callers cache the returned view per `(slot, offset)` pair so the\n * view's identity — and therefore its recorded offset — stays stable\n * across bars.\n *\n * @since 0.2\n * @stable\n * @example\n * // import { Float64RingBuffer, makeShiftedSeriesView, seriesOffsetOf }\n * // from \"@invinite-org/chartlang-runtime\";\n * // const buf = new Float64RingBuffer(8);\n * // buf.append(10); buf.append(20); buf.append(30);\n * // const view = makeShiftedSeriesView<number>(buf, 5);\n * // view.current; // 30 (unshifted the offset does NOT lag the read)\n * // seriesOffsetOf(view); // 5 (presentation x-shift carried to the emission)\n */\nexport function makeShiftedSeriesView<T>(buf: RingBufferLike<T>, offset: number): Series<T> {\n const view = makeSeriesView<T>(buf);\n if (offset !== 0) seriesOffsets.set(view, offset);\n return view;\n}\n\n/**\n * Read the presentation x-shift recorded for `series` by\n * {@link makeShiftedSeriesView}, or `0` when the series is untagged (a\n * plain {@link makeSeriesView} view, an `offset === 0` view, or any\n * non-runtime Series). `plot()` calls this to populate\n * `PlotEmission.xShift`; a `0` result omits the field so a no-offset\n * plot stays byte-identical to today.\n *\n * @since 0.2\n * @stable\n * @example\n * // import { Float64RingBuffer, makeShiftedSeriesView, seriesOffsetOf }\n * // from \"@invinite-org/chartlang-runtime\";\n * // const buf = new Float64RingBuffer(8);\n * // const shifted = makeShiftedSeriesView<number>(buf, -3);\n * // seriesOffsetOf(shifted); // -3\n */\nexport function seriesOffsetOf(series: Series<unknown>): number {\n return seriesOffsets.get(series) ?? 0;\n}\n"]}
@@ -1,5 +1,6 @@
1
- export { commitStateSlots, flushStateSlots, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
1
+ export { advanceSeriesSlots, commitSeriesSlots, commitStateSlots, flushStateSlots, resetSeriesHeads, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
2
2
  export type { StateSlotSnapshot } from "./lifecycle.js";
3
+ export { isSeriesSlotSnapshotKey, restoreSeriesSlots, serialiseSeriesSlots, } from "./seriesPersistence.js";
3
4
  export { buildStateNamespace } from "./stateNamespace.js";
4
5
  export type { StateSlotSerialisers } from "./stateSlot.js";
5
6
  export { asMutableSlot, StateSlot } from "./stateSlot.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACH,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
2
  // See the LICENSE file in the repo root for full license text.
3
- export { commitStateSlots, flushStateSlots, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
3
+ export { advanceSeriesSlots, commitSeriesSlots, commitStateSlots, flushStateSlots, resetSeriesHeads, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
4
+ export { isSeriesSlotSnapshotKey, restoreSeriesSlots, serialiseSeriesSlots, } from "./seriesPersistence.js";
4
5
  export { buildStateNamespace } from "./stateNamespace.js";
5
6
  export { asMutableSlot, StateSlot } from "./stateSlot.js";
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAE/D,OAAO,EACH,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,GACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nexport {\n commitStateSlots,\n flushStateSlots,\n restoreStateSlots,\n resetTentativeStateSlots,\n serialiseStateSlots,\n} from \"./lifecycle.js\";\nexport type { StateSlotSnapshot } from \"./lifecycle.js\";\nexport { buildStateNamespace } from \"./stateNamespace.js\";\nexport type { StateSlotSerialisers } from \"./stateSlot.js\";\nexport { asMutableSlot, StateSlot } from \"./stateSlot.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAE/D,OAAO,EACH,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,GACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACH,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nexport {\n advanceSeriesSlots,\n commitSeriesSlots,\n commitStateSlots,\n flushStateSlots,\n resetSeriesHeads,\n restoreStateSlots,\n resetTentativeStateSlots,\n serialiseStateSlots,\n} from \"./lifecycle.js\";\nexport type { StateSlotSnapshot } from \"./lifecycle.js\";\nexport {\n isSeriesSlotSnapshotKey,\n restoreSeriesSlots,\n serialiseSeriesSlots,\n} from \"./seriesPersistence.js\";\nexport { buildStateNamespace } from \"./stateNamespace.js\";\nexport type { StateSlotSerialisers } from \"./stateSlot.js\";\nexport { asMutableSlot, StateSlot } from \"./stateSlot.js\";\n"]}
@@ -72,4 +72,44 @@ export declare function serialiseStateSlots(ctx: RuntimeContext): Readonly<Recor
72
72
  * void restored;
73
73
  */
74
74
  export declare function restoreStateSlots(ctx: RuntimeContext, slots: Readonly<Record<string, unknown>>): void;
75
+ /**
76
+ * Advance every `state.series` ring once for a new close bar — append a
77
+ * fresh `NaN` head so the prior committed head slides to index 1. Runs
78
+ * BEFORE compute on close, so a slot first allocated mid-compute (already
79
+ * holding its seeded head) is not present here and is not double-advanced.
80
+ *
81
+ * @since 0.9
82
+ * @stable
83
+ * @example
84
+ * // advanceSeriesSlots(ctx);
85
+ * const advanced = true;
86
+ * void advanced;
87
+ */
88
+ export declare function advanceSeriesSlots(ctx: RuntimeContext): void;
89
+ /**
90
+ * Commit every `state.series` live head as its bar-close value after
91
+ * close compute, so the next advance retains it and a tick can reset to
92
+ * it.
93
+ *
94
+ * @since 0.9
95
+ * @stable
96
+ * @example
97
+ * // commitSeriesSlots(ctx);
98
+ * const committed = true;
99
+ * void committed;
100
+ */
101
+ export declare function commitSeriesSlots(ctx: RuntimeContext): void;
102
+ /**
103
+ * Reset every `state.series` live head to its last committed value before
104
+ * tick compute, so a re-write refines from the committed baseline and a
105
+ * tick without a write reads the committed head. Does NOT advance length.
106
+ *
107
+ * @since 0.9
108
+ * @stable
109
+ * @example
110
+ * // resetSeriesHeads(ctx);
111
+ * const reset = true;
112
+ * void reset;
113
+ */
114
+ export declare function resetSeriesHeads(ctx: RuntimeContext): void;
75
115
  //# sourceMappingURL=lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/state/lifecycle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IAC/B,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAIlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAI1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAOzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAS1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,IAAI,CAKN"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/state/lifecycle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IAC/B,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAIlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAI1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAOzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAS1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,IAAI,CAKN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAI5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAI3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAI1D"}
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
2
  // See the LICENSE file in the repo root for full license text.
3
+ import { advanceSeriesSlot, commitSeriesSlot, resetSeriesSlotHead } from "./seriesSlot.js";
3
4
  /**
4
5
  * Reset all non-`state.tick.*` tentative values before tick compute.
5
6
  *
@@ -84,4 +85,56 @@ export function restoreStateSlots(ctx, slots) {
84
85
  ctx.stateStore.set(key, value);
85
86
  }
86
87
  }
88
+ /**
89
+ * Advance every `state.series` ring once for a new close bar — append a
90
+ * fresh `NaN` head so the prior committed head slides to index 1. Runs
91
+ * BEFORE compute on close, so a slot first allocated mid-compute (already
92
+ * holding its seeded head) is not present here and is not double-advanced.
93
+ *
94
+ * @since 0.9
95
+ * @stable
96
+ * @example
97
+ * // advanceSeriesSlots(ctx);
98
+ * const advanced = true;
99
+ * void advanced;
100
+ */
101
+ export function advanceSeriesSlots(ctx) {
102
+ for (const slot of ctx.seriesSlots.values()) {
103
+ advanceSeriesSlot(slot);
104
+ }
105
+ }
106
+ /**
107
+ * Commit every `state.series` live head as its bar-close value after
108
+ * close compute, so the next advance retains it and a tick can reset to
109
+ * it.
110
+ *
111
+ * @since 0.9
112
+ * @stable
113
+ * @example
114
+ * // commitSeriesSlots(ctx);
115
+ * const committed = true;
116
+ * void committed;
117
+ */
118
+ export function commitSeriesSlots(ctx) {
119
+ for (const slot of ctx.seriesSlots.values()) {
120
+ commitSeriesSlot(slot);
121
+ }
122
+ }
123
+ /**
124
+ * Reset every `state.series` live head to its last committed value before
125
+ * tick compute, so a re-write refines from the committed baseline and a
126
+ * tick without a write reads the committed head. Does NOT advance length.
127
+ *
128
+ * @since 0.9
129
+ * @stable
130
+ * @example
131
+ * // resetSeriesHeads(ctx);
132
+ * const reset = true;
133
+ * void reset;
134
+ */
135
+ export function resetSeriesHeads(ctx) {
136
+ for (const slot of ctx.seriesSlots.values()) {
137
+ resetSeriesSlotHead(slot);
138
+ }
139
+ }
87
140
  //# sourceMappingURL=lifecycle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/state/lifecycle.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAuB/D;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAmB;IACxD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAChD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,GAAmB;IAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,UAAU,CAAC,GAAG,CAA6B,GAAG,EAAE;YAChD,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC5B,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,GAAG;YACP,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;YACzC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;SAC5C,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC7B,GAAmB,EACnB,KAAwC;IAExC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;AACL,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { RuntimeContext } from \"../runtimeContext.js\";\n\n/**\n * Persisted representation for a runtime state slot. Keys use\n * `${slotId}:state`, matching the compiler-injected slot id plus the\n * state namespace suffix.\n *\n * @since 0.4\n * @stable\n * @example\n * const snapshot: StateSlotSnapshot<number> = {\n * committed: 1,\n * tentative: 2,\n * };\n * void snapshot;\n */\nexport type StateSlotSnapshot<T> = {\n readonly committed: T;\n readonly tentative: T;\n};\n\n/**\n * Reset all non-`state.tick.*` tentative values before tick compute.\n *\n * @since 0.4\n * @stable\n * @example\n * // resetTentativeStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function resetTentativeStateSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.stateSlots.values()) {\n slot.onBarTick();\n }\n}\n\n/**\n * Commit all non-`state.tick.*` tentative values after close compute.\n *\n * @since 0.4\n * @stable\n * @example\n * // commitStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function commitStateSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.stateSlots.values()) {\n slot.onBarClose();\n }\n}\n\n/**\n * Flush runtime state slots into the backing {@link StateStore}.\n *\n * @since 0.4\n * @stable\n * @example\n * // flushStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function flushStateSlots(ctx: RuntimeContext): void {\n for (const [key, slot] of ctx.stateSlots.entries()) {\n ctx.stateStore.set<StateSlotSnapshot<unknown>>(key, {\n committed: slot.committed,\n tentative: slot.tentative,\n });\n }\n}\n\n/**\n * Serialise runtime state slots into a snapshot payload.\n *\n * @since 0.5\n * @stable\n * @example\n * // const slots = serialiseStateSlots(ctx);\n * const slots = {};\n * void slots;\n */\nexport function serialiseStateSlots(ctx: RuntimeContext): Readonly<Record<string, unknown>> {\n const out: Record<string, unknown> = {};\n for (const [key, slot] of ctx.stateSlots.entries()) {\n out[key] = {\n committed: slot.serialise(slot.committed),\n tentative: slot.serialise(slot.tentative),\n };\n }\n return Object.freeze(out);\n}\n\n/**\n * Seed restored state-slot payloads into the backing slot store.\n *\n * @since 0.5\n * @stable\n * @example\n * // restoreStateSlots(ctx, snapshot.slots);\n * const restored = true;\n * void restored;\n */\nexport function restoreStateSlots(\n ctx: RuntimeContext,\n slots: Readonly<Record<string, unknown>>,\n): void {\n ctx.stateSlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n ctx.stateStore.set(key, value);\n }\n}\n"]}
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/state/lifecycle.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAG/D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAqB3F;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAmB;IACxD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAChD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,GAAmB;IAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,UAAU,CAAC,GAAG,CAA6B,GAAG,EAAE;YAChD,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC5B,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,GAAG;YACP,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;YACzC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;SAC5C,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC7B,GAAmB,EACnB,KAAwC;IAExC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAmB;IAClD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAmB;IACjD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAChD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { RuntimeContext } from \"../runtimeContext.js\";\nimport { advanceSeriesSlot, commitSeriesSlot, resetSeriesSlotHead } from \"./seriesSlot.js\";\n\n/**\n * Persisted representation for a runtime state slot. Keys use\n * `${slotId}:state`, matching the compiler-injected slot id plus the\n * state namespace suffix.\n *\n * @since 0.4\n * @stable\n * @example\n * const snapshot: StateSlotSnapshot<number> = {\n * committed: 1,\n * tentative: 2,\n * };\n * void snapshot;\n */\nexport type StateSlotSnapshot<T> = {\n readonly committed: T;\n readonly tentative: T;\n};\n\n/**\n * Reset all non-`state.tick.*` tentative values before tick compute.\n *\n * @since 0.4\n * @stable\n * @example\n * // resetTentativeStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function resetTentativeStateSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.stateSlots.values()) {\n slot.onBarTick();\n }\n}\n\n/**\n * Commit all non-`state.tick.*` tentative values after close compute.\n *\n * @since 0.4\n * @stable\n * @example\n * // commitStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function commitStateSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.stateSlots.values()) {\n slot.onBarClose();\n }\n}\n\n/**\n * Flush runtime state slots into the backing {@link StateStore}.\n *\n * @since 0.4\n * @stable\n * @example\n * // flushStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function flushStateSlots(ctx: RuntimeContext): void {\n for (const [key, slot] of ctx.stateSlots.entries()) {\n ctx.stateStore.set<StateSlotSnapshot<unknown>>(key, {\n committed: slot.committed,\n tentative: slot.tentative,\n });\n }\n}\n\n/**\n * Serialise runtime state slots into a snapshot payload.\n *\n * @since 0.5\n * @stable\n * @example\n * // const slots = serialiseStateSlots(ctx);\n * const slots = {};\n * void slots;\n */\nexport function serialiseStateSlots(ctx: RuntimeContext): Readonly<Record<string, unknown>> {\n const out: Record<string, unknown> = {};\n for (const [key, slot] of ctx.stateSlots.entries()) {\n out[key] = {\n committed: slot.serialise(slot.committed),\n tentative: slot.serialise(slot.tentative),\n };\n }\n return Object.freeze(out);\n}\n\n/**\n * Seed restored state-slot payloads into the backing slot store.\n *\n * @since 0.5\n * @stable\n * @example\n * // restoreStateSlots(ctx, snapshot.slots);\n * const restored = true;\n * void restored;\n */\nexport function restoreStateSlots(\n ctx: RuntimeContext,\n slots: Readonly<Record<string, unknown>>,\n): void {\n ctx.stateSlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n ctx.stateStore.set(key, value);\n }\n}\n\n/**\n * Advance every `state.series` ring once for a new close bar — append a\n * fresh `NaN` head so the prior committed head slides to index 1. Runs\n * BEFORE compute on close, so a slot first allocated mid-compute (already\n * holding its seeded head) is not present here and is not double-advanced.\n *\n * @since 0.9\n * @stable\n * @example\n * // advanceSeriesSlots(ctx);\n * const advanced = true;\n * void advanced;\n */\nexport function advanceSeriesSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.seriesSlots.values()) {\n advanceSeriesSlot(slot);\n }\n}\n\n/**\n * Commit every `state.series` live head as its bar-close value after\n * close compute, so the next advance retains it and a tick can reset to\n * it.\n *\n * @since 0.9\n * @stable\n * @example\n * // commitSeriesSlots(ctx);\n * const committed = true;\n * void committed;\n */\nexport function commitSeriesSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.seriesSlots.values()) {\n commitSeriesSlot(slot);\n }\n}\n\n/**\n * Reset every `state.series` live head to its last committed value before\n * tick compute, so a re-write refines from the committed baseline and a\n * tick without a write reads the committed head. Does NOT advance length.\n *\n * @since 0.9\n * @stable\n * @example\n * // resetSeriesHeads(ctx);\n * const reset = true;\n * void reset;\n */\nexport function resetSeriesHeads(ctx: RuntimeContext): void {\n for (const slot of ctx.seriesSlots.values()) {\n resetSeriesSlotHead(slot);\n }\n}\n"]}
@@ -0,0 +1,48 @@
1
+ import type { JsonValue } from "@invinite-org/chartlang-core";
2
+ import type { RuntimeContext } from "../runtimeContext.js";
3
+ /**
4
+ * Return whether a snapshot slot key belongs to the `state.series`
5
+ * namespace (a `${slotIdPrefix}${slotId}:series` key). Lets the restore
6
+ * router separate series slots from scalar `state.*` (`:state`) and `ta.*`
7
+ * (`ta:`) slots, all of which share one `slots` record.
8
+ *
9
+ * @since 0.9
10
+ * @internal
11
+ * @stable
12
+ * @example
13
+ * isSeriesSlotSnapshotKey("x.chart.ts:1:1:series"); // true
14
+ */
15
+ export declare function isSeriesSlotSnapshotKey(key: string): boolean;
16
+ /**
17
+ * Serialise the runner's `state.series` slots into JSON-clean snapshot
18
+ * entries keyed by the same `${prefix}${slotId}:series` key the slot store
19
+ * uses. `committedHead` is nulled when `NaN` (the validator rejects `NaN`);
20
+ * the buffer rides {@link Float64RingBuffer.serialiseSnapshotBuffer}.
21
+ *
22
+ * @since 0.9
23
+ * @internal
24
+ * @stable
25
+ * @example
26
+ * // const entries = serialiseSeriesSlots(ctx);
27
+ * const entries = {};
28
+ * void entries;
29
+ */
30
+ export declare function serialiseSeriesSlots(ctx: RuntimeContext): Readonly<Record<string, JsonValue>>;
31
+ /**
32
+ * Restore `state.series` slots from namespaced snapshot entries into
33
+ * `ctx.seriesSlots`, rebuilding each ring via
34
+ * {@link Float64RingBuffer.restoreFromSnapshotBuffer} sized to the runner's
35
+ * current `capacity`. Non-series keys are ignored; malformed series
36
+ * snapshots are skipped. The view identity is recreated on restore
37
+ * (acceptable — same as `ta.*`).
38
+ *
39
+ * @since 0.9
40
+ * @internal
41
+ * @stable
42
+ * @example
43
+ * // restoreSeriesSlots(ctx, snapshot.slots, capacity);
44
+ * const restored = true;
45
+ * void restored;
46
+ */
47
+ export declare function restoreSeriesSlots(ctx: RuntimeContext, slots: Readonly<Record<string, unknown>>, capacity: number): void;
48
+ //# sourceMappingURL=seriesPersistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seriesPersistence.d.ts","sourceRoot":"","sources":["../../src/state/seriesPersistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAU9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAU7F;AAaD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,QAAQ,EAAE,MAAM,GACjB,IAAI,CASN"}
@@ -0,0 +1,87 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ import { finiteOrNull, isBufferSnapshot, isRecord, restoreBuffer, restoreNumber, serialiseBuffer, } from "../bufferSnapshot.js";
4
+ import { restoreSeriesSlot } from "./seriesSlot.js";
5
+ const SERIES_SLOT_SUFFIX = ":series";
6
+ /**
7
+ * Return whether a snapshot slot key belongs to the `state.series`
8
+ * namespace (a `${slotIdPrefix}${slotId}:series` key). Lets the restore
9
+ * router separate series slots from scalar `state.*` (`:state`) and `ta.*`
10
+ * (`ta:`) slots, all of which share one `slots` record.
11
+ *
12
+ * @since 0.9
13
+ * @internal
14
+ * @stable
15
+ * @example
16
+ * isSeriesSlotSnapshotKey("x.chart.ts:1:1:series"); // true
17
+ */
18
+ export function isSeriesSlotSnapshotKey(key) {
19
+ return key.endsWith(SERIES_SLOT_SUFFIX);
20
+ }
21
+ /**
22
+ * Serialise the runner's `state.series` slots into JSON-clean snapshot
23
+ * entries keyed by the same `${prefix}${slotId}:series` key the slot store
24
+ * uses. `committedHead` is nulled when `NaN` (the validator rejects `NaN`);
25
+ * the buffer rides {@link Float64RingBuffer.serialiseSnapshotBuffer}.
26
+ *
27
+ * @since 0.9
28
+ * @internal
29
+ * @stable
30
+ * @example
31
+ * // const entries = serialiseSeriesSlots(ctx);
32
+ * const entries = {};
33
+ * void entries;
34
+ */
35
+ export function serialiseSeriesSlots(ctx) {
36
+ const out = {};
37
+ for (const [key, slot] of ctx.seriesSlots.entries()) {
38
+ out[key] = {
39
+ kind: "state.series",
40
+ buffer: serialiseBuffer(slot.buffer),
41
+ committedHead: finiteOrNull(slot.committedHead),
42
+ };
43
+ }
44
+ return Object.freeze(out);
45
+ }
46
+ function restoreSeriesSlotSnapshot(snapshot, capacity) {
47
+ if (!isRecord(snapshot) || snapshot.kind !== "state.series")
48
+ return null;
49
+ const bufferSnapshot = snapshot.buffer;
50
+ if (!isBufferSnapshot(bufferSnapshot))
51
+ return null;
52
+ const committedHead = restoreNumber(snapshot.committedHead);
53
+ if (committedHead === null)
54
+ return null;
55
+ const buffer = restoreBuffer(bufferSnapshot, capacity);
56
+ if (buffer === null)
57
+ return null;
58
+ return restoreSeriesSlot(buffer, committedHead);
59
+ }
60
+ /**
61
+ * Restore `state.series` slots from namespaced snapshot entries into
62
+ * `ctx.seriesSlots`, rebuilding each ring via
63
+ * {@link Float64RingBuffer.restoreFromSnapshotBuffer} sized to the runner's
64
+ * current `capacity`. Non-series keys are ignored; malformed series
65
+ * snapshots are skipped. The view identity is recreated on restore
66
+ * (acceptable — same as `ta.*`).
67
+ *
68
+ * @since 0.9
69
+ * @internal
70
+ * @stable
71
+ * @example
72
+ * // restoreSeriesSlots(ctx, snapshot.slots, capacity);
73
+ * const restored = true;
74
+ * void restored;
75
+ */
76
+ export function restoreSeriesSlots(ctx, slots, capacity) {
77
+ ctx.seriesSlots.clear();
78
+ for (const [key, value] of Object.entries(slots)) {
79
+ if (!isSeriesSlotSnapshotKey(key))
80
+ continue;
81
+ const slot = restoreSeriesSlotSnapshot(value, capacity);
82
+ if (slot !== null) {
83
+ ctx.seriesSlots.set(key, slot);
84
+ }
85
+ }
86
+ }
87
+ //# sourceMappingURL=seriesPersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seriesPersistence.js","sourceRoot":"","sources":["../../src/state/seriesPersistence.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D,OAAO,EACH,YAAY,EACZ,gBAAgB,EAChB,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAmB,MAAM,iBAAiB,CAAC;AAErE,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAW;IAC/C,OAAO,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAmB;IACpD,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;QAClD,GAAG,CAAC,GAAG,CAAC,GAAG;YACP,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;YACpC,aAAa,EAAE,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC;SAClD,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,yBAAyB,CAAC,QAAiB,EAAE,QAAgB;IAClE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IACzE,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;IACvC,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC5D,IAAI,aAAa,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IACvD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,iBAAiB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAC9B,GAAmB,EACnB,KAAwC,EACxC,QAAgB;IAEhB,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5C,MAAM,IAAI,GAAG,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAChB,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;AACL,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { JsonValue } from \"@invinite-org/chartlang-core\";\n\nimport {\n finiteOrNull,\n isBufferSnapshot,\n isRecord,\n restoreBuffer,\n restoreNumber,\n serialiseBuffer,\n} from \"../bufferSnapshot.js\";\nimport type { RuntimeContext } from \"../runtimeContext.js\";\nimport { restoreSeriesSlot, type SeriesSlot } from \"./seriesSlot.js\";\n\nconst SERIES_SLOT_SUFFIX = \":series\";\n\n/**\n * Return whether a snapshot slot key belongs to the `state.series`\n * namespace (a `${slotIdPrefix}${slotId}:series` key). Lets the restore\n * router separate series slots from scalar `state.*` (`:state`) and `ta.*`\n * (`ta:`) slots, all of which share one `slots` record.\n *\n * @since 0.9\n * @internal\n * @stable\n * @example\n * isSeriesSlotSnapshotKey(\"x.chart.ts:1:1:series\"); // true\n */\nexport function isSeriesSlotSnapshotKey(key: string): boolean {\n return key.endsWith(SERIES_SLOT_SUFFIX);\n}\n\n/**\n * Serialise the runner's `state.series` slots into JSON-clean snapshot\n * entries keyed by the same `${prefix}${slotId}:series` key the slot store\n * uses. `committedHead` is nulled when `NaN` (the validator rejects `NaN`);\n * the buffer rides {@link Float64RingBuffer.serialiseSnapshotBuffer}.\n *\n * @since 0.9\n * @internal\n * @stable\n * @example\n * // const entries = serialiseSeriesSlots(ctx);\n * const entries = {};\n * void entries;\n */\nexport function serialiseSeriesSlots(ctx: RuntimeContext): Readonly<Record<string, JsonValue>> {\n const out: Record<string, JsonValue> = {};\n for (const [key, slot] of ctx.seriesSlots.entries()) {\n out[key] = {\n kind: \"state.series\",\n buffer: serialiseBuffer(slot.buffer),\n committedHead: finiteOrNull(slot.committedHead),\n };\n }\n return Object.freeze(out);\n}\n\nfunction restoreSeriesSlotSnapshot(snapshot: unknown, capacity: number): SeriesSlot | null {\n if (!isRecord(snapshot) || snapshot.kind !== \"state.series\") return null;\n const bufferSnapshot = snapshot.buffer;\n if (!isBufferSnapshot(bufferSnapshot)) return null;\n const committedHead = restoreNumber(snapshot.committedHead);\n if (committedHead === null) return null;\n const buffer = restoreBuffer(bufferSnapshot, capacity);\n if (buffer === null) return null;\n return restoreSeriesSlot(buffer, committedHead);\n}\n\n/**\n * Restore `state.series` slots from namespaced snapshot entries into\n * `ctx.seriesSlots`, rebuilding each ring via\n * {@link Float64RingBuffer.restoreFromSnapshotBuffer} sized to the runner's\n * current `capacity`. Non-series keys are ignored; malformed series\n * snapshots are skipped. The view identity is recreated on restore\n * (acceptable — same as `ta.*`).\n *\n * @since 0.9\n * @internal\n * @stable\n * @example\n * // restoreSeriesSlots(ctx, snapshot.slots, capacity);\n * const restored = true;\n * void restored;\n */\nexport function restoreSeriesSlots(\n ctx: RuntimeContext,\n slots: Readonly<Record<string, unknown>>,\n capacity: number,\n): void {\n ctx.seriesSlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n if (!isSeriesSlotSnapshotKey(key)) continue;\n const slot = restoreSeriesSlotSnapshot(value, capacity);\n if (slot !== null) {\n ctx.seriesSlots.set(key, slot);\n }\n }\n}\n"]}