@invinite-org/chartlang-runtime 1.1.1 → 1.3.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 (294) hide show
  1. package/CHANGELOG.md +307 -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/buildComputeContext.d.ts.map +1 -1
  11. package/dist/buildComputeContext.js +6 -1
  12. package/dist/buildComputeContext.js.map +1 -1
  13. package/dist/createScriptRunner.d.ts +6 -3
  14. package/dist/createScriptRunner.d.ts.map +1 -1
  15. package/dist/createScriptRunner.js +65 -11
  16. package/dist/createScriptRunner.js.map +1 -1
  17. package/dist/dep/DepRunner.d.ts +7 -0
  18. package/dist/dep/DepRunner.d.ts.map +1 -1
  19. package/dist/dep/DepRunner.js +4 -0
  20. package/dist/dep/DepRunner.js.map +1 -1
  21. package/dist/emit/barcolor.d.ts +44 -0
  22. package/dist/emit/barcolor.d.ts.map +1 -0
  23. package/dist/emit/barcolor.js +40 -0
  24. package/dist/emit/barcolor.js.map +1 -0
  25. package/dist/emit/bgcolor.d.ts +44 -0
  26. package/dist/emit/bgcolor.d.ts.map +1 -0
  27. package/dist/emit/bgcolor.js +45 -0
  28. package/dist/emit/bgcolor.js.map +1 -0
  29. package/dist/emit/draw/boxes/fillBetween.d.ts +45 -0
  30. package/dist/emit/draw/boxes/fillBetween.d.ts.map +1 -0
  31. package/dist/emit/draw/boxes/fillBetween.js +36 -0
  32. package/dist/emit/draw/boxes/fillBetween.js.map +1 -0
  33. package/dist/emit/draw/handle.d.ts +9 -0
  34. package/dist/emit/draw/handle.d.ts.map +1 -1
  35. package/dist/emit/draw/handle.js +65 -10
  36. package/dist/emit/draw/handle.js.map +1 -1
  37. package/dist/emit/draw/namespace.d.ts +4 -3
  38. package/dist/emit/draw/namespace.d.ts.map +1 -1
  39. package/dist/emit/draw/namespace.js +6 -3
  40. package/dist/emit/draw/namespace.js.map +1 -1
  41. package/dist/emit/index.d.ts +2 -0
  42. package/dist/emit/index.d.ts.map +1 -1
  43. package/dist/emit/index.js +2 -0
  44. package/dist/emit/index.js.map +1 -1
  45. package/dist/emit/plot.d.ts +30 -1
  46. package/dist/emit/plot.d.ts.map +1 -1
  47. package/dist/emit/plot.js +45 -1
  48. package/dist/emit/plot.js.map +1 -1
  49. package/dist/execution/dispose.d.ts.map +1 -1
  50. package/dist/execution/dispose.js +18 -0
  51. package/dist/execution/dispose.js.map +1 -1
  52. package/dist/execution/runComputeStep.d.ts.map +1 -1
  53. package/dist/execution/runComputeStep.js +12 -1
  54. package/dist/execution/runComputeStep.js.map +1 -1
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/inputs/resolveInputs.js +1 -0
  60. package/dist/inputs/resolveInputs.js.map +1 -1
  61. package/dist/persistentStateStore.runtime.d.ts.map +1 -1
  62. package/dist/persistentStateStore.runtime.js +26 -7
  63. package/dist/persistentStateStore.runtime.js.map +1 -1
  64. package/dist/primitives.d.ts +1 -1
  65. package/dist/primitives.d.ts.map +1 -1
  66. package/dist/primitives.js +7 -1
  67. package/dist/primitives.js.map +1 -1
  68. package/dist/request/index.d.ts +2 -1
  69. package/dist/request/index.d.ts.map +1 -1
  70. package/dist/request/index.js +2 -1
  71. package/dist/request/index.js.map +1 -1
  72. package/dist/request/lowerTf.d.ts.map +1 -1
  73. package/dist/request/lowerTf.js +6 -0
  74. package/dist/request/lowerTf.js.map +1 -1
  75. package/dist/request/requestNamespace.d.ts.map +1 -1
  76. package/dist/request/requestNamespace.js +30 -3
  77. package/dist/request/requestNamespace.js.map +1 -1
  78. package/dist/request/security.d.ts +40 -4
  79. package/dist/request/security.d.ts.map +1 -1
  80. package/dist/request/security.js +114 -40
  81. package/dist/request/security.js.map +1 -1
  82. package/dist/request/securityExprRunner.d.ts +137 -0
  83. package/dist/request/securityExprRunner.d.ts.map +1 -0
  84. package/dist/request/securityExprRunner.js +253 -0
  85. package/dist/request/securityExprRunner.js.map +1 -0
  86. package/dist/request/streamBars.d.ts +14 -1
  87. package/dist/request/streamBars.d.ts.map +1 -1
  88. package/dist/request/streamBars.js +39 -1
  89. package/dist/request/streamBars.js.map +1 -1
  90. package/dist/ringBuffer.d.ts +19 -0
  91. package/dist/ringBuffer.d.ts.map +1 -1
  92. package/dist/ringBuffer.js +23 -0
  93. package/dist/ringBuffer.js.map +1 -1
  94. package/dist/runtimeContext.d.ts +90 -5
  95. package/dist/runtimeContext.d.ts.map +1 -1
  96. package/dist/runtimeContext.js.map +1 -1
  97. package/dist/seriesView.d.ts +42 -17
  98. package/dist/seriesView.d.ts.map +1 -1
  99. package/dist/seriesView.js +65 -42
  100. package/dist/seriesView.js.map +1 -1
  101. package/dist/state/arrayPersistence.d.ts +48 -0
  102. package/dist/state/arrayPersistence.d.ts.map +1 -0
  103. package/dist/state/arrayPersistence.js +88 -0
  104. package/dist/state/arrayPersistence.js.map +1 -0
  105. package/dist/state/arrayStateSlot.d.ts +78 -0
  106. package/dist/state/arrayStateSlot.d.ts.map +1 -0
  107. package/dist/state/arrayStateSlot.js +116 -0
  108. package/dist/state/arrayStateSlot.js.map +1 -0
  109. package/dist/state/index.d.ts +4 -1
  110. package/dist/state/index.d.ts.map +1 -1
  111. package/dist/state/index.js +4 -1
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/state/lifecycle.d.ts +68 -0
  114. package/dist/state/lifecycle.d.ts.map +1 -1
  115. package/dist/state/lifecycle.js +89 -0
  116. package/dist/state/lifecycle.js.map +1 -1
  117. package/dist/state/seriesPersistence.d.ts +48 -0
  118. package/dist/state/seriesPersistence.d.ts.map +1 -0
  119. package/dist/state/seriesPersistence.js +87 -0
  120. package/dist/state/seriesPersistence.js.map +1 -0
  121. package/dist/state/seriesSlot.d.ts +105 -0
  122. package/dist/state/seriesSlot.d.ts.map +1 -0
  123. package/dist/state/seriesSlot.js +123 -0
  124. package/dist/state/seriesSlot.js.map +1 -0
  125. package/dist/state/stateNamespace.d.ts.map +1 -1
  126. package/dist/state/stateNamespace.js +55 -0
  127. package/dist/state/stateNamespace.js.map +1 -1
  128. package/dist/streamState.d.ts +25 -19
  129. package/dist/streamState.d.ts.map +1 -1
  130. package/dist/streamState.js +40 -66
  131. package/dist/streamState.js.map +1 -1
  132. package/dist/ta/adx.d.ts +3 -2
  133. package/dist/ta/adx.d.ts.map +1 -1
  134. package/dist/ta/adx.js +3 -2
  135. package/dist/ta/adx.js.map +1 -1
  136. package/dist/ta/alma.d.ts +6 -4
  137. package/dist/ta/alma.d.ts.map +1 -1
  138. package/dist/ta/alma.js +19 -6
  139. package/dist/ta/alma.js.map +1 -1
  140. package/dist/ta/atr.d.ts +3 -2
  141. package/dist/ta/atr.d.ts.map +1 -1
  142. package/dist/ta/atr.js +3 -2
  143. package/dist/ta/atr.js.map +1 -1
  144. package/dist/ta/bb.d.ts +3 -2
  145. package/dist/ta/bb.d.ts.map +1 -1
  146. package/dist/ta/bb.js +3 -2
  147. package/dist/ta/bb.js.map +1 -1
  148. package/dist/ta/chaikinOsc.d.ts +3 -2
  149. package/dist/ta/chaikinOsc.d.ts.map +1 -1
  150. package/dist/ta/chaikinOsc.js +3 -2
  151. package/dist/ta/chaikinOsc.js.map +1 -1
  152. package/dist/ta/crossover.d.ts +3 -2
  153. package/dist/ta/crossover.d.ts.map +1 -1
  154. package/dist/ta/crossover.js +3 -2
  155. package/dist/ta/crossover.js.map +1 -1
  156. package/dist/ta/crossunder.d.ts +3 -2
  157. package/dist/ta/crossunder.d.ts.map +1 -1
  158. package/dist/ta/crossunder.js +3 -2
  159. package/dist/ta/crossunder.js.map +1 -1
  160. package/dist/ta/dmi.d.ts +4 -3
  161. package/dist/ta/dmi.d.ts.map +1 -1
  162. package/dist/ta/dmi.js +4 -3
  163. package/dist/ta/dmi.js.map +1 -1
  164. package/dist/ta/ema.d.ts +3 -2
  165. package/dist/ta/ema.d.ts.map +1 -1
  166. package/dist/ta/ema.js +3 -2
  167. package/dist/ta/ema.js.map +1 -1
  168. package/dist/ta/eom.d.ts +3 -1
  169. package/dist/ta/eom.d.ts.map +1 -1
  170. package/dist/ta/eom.js +3 -1
  171. package/dist/ta/eom.js.map +1 -1
  172. package/dist/ta/highestbars.d.ts +25 -0
  173. package/dist/ta/highestbars.d.ts.map +1 -0
  174. package/dist/ta/highestbars.js +106 -0
  175. package/dist/ta/highestbars.js.map +1 -0
  176. package/dist/ta/historicalVolatility.d.ts +3 -2
  177. package/dist/ta/historicalVolatility.d.ts.map +1 -1
  178. package/dist/ta/historicalVolatility.js +3 -2
  179. package/dist/ta/historicalVolatility.js.map +1 -1
  180. package/dist/ta/ichimoku.d.ts +3 -1
  181. package/dist/ta/ichimoku.d.ts.map +1 -1
  182. package/dist/ta/ichimoku.js +3 -1
  183. package/dist/ta/ichimoku.js.map +1 -1
  184. package/dist/ta/lowestbars.d.ts +25 -0
  185. package/dist/ta/lowestbars.d.ts.map +1 -0
  186. package/dist/ta/lowestbars.js +102 -0
  187. package/dist/ta/lowestbars.js.map +1 -0
  188. package/dist/ta/macd.d.ts +3 -2
  189. package/dist/ta/macd.d.ts.map +1 -1
  190. package/dist/ta/macd.js +3 -2
  191. package/dist/ta/macd.js.map +1 -1
  192. package/dist/ta/massIndex.d.ts +3 -2
  193. package/dist/ta/massIndex.d.ts.map +1 -1
  194. package/dist/ta/massIndex.js +3 -2
  195. package/dist/ta/massIndex.js.map +1 -1
  196. package/dist/ta/mfi.d.ts +3 -1
  197. package/dist/ta/mfi.d.ts.map +1 -1
  198. package/dist/ta/mfi.js +3 -1
  199. package/dist/ta/mfi.js.map +1 -1
  200. package/dist/ta/netVolume.d.ts +3 -1
  201. package/dist/ta/netVolume.d.ts.map +1 -1
  202. package/dist/ta/netVolume.js +3 -1
  203. package/dist/ta/netVolume.js.map +1 -1
  204. package/dist/ta/nvi.d.ts +3 -1
  205. package/dist/ta/nvi.d.ts.map +1 -1
  206. package/dist/ta/nvi.js +3 -1
  207. package/dist/ta/nvi.js.map +1 -1
  208. package/dist/ta/persistence.d.ts.map +1 -1
  209. package/dist/ta/persistence.js +1 -40
  210. package/dist/ta/persistence.js.map +1 -1
  211. package/dist/ta/ppo.d.ts +3 -2
  212. package/dist/ta/ppo.d.ts.map +1 -1
  213. package/dist/ta/ppo.js +3 -2
  214. package/dist/ta/ppo.js.map +1 -1
  215. package/dist/ta/pvi.d.ts +3 -1
  216. package/dist/ta/pvi.d.ts.map +1 -1
  217. package/dist/ta/pvi.js +3 -1
  218. package/dist/ta/pvi.js.map +1 -1
  219. package/dist/ta/pvo.d.ts +3 -1
  220. package/dist/ta/pvo.d.ts.map +1 -1
  221. package/dist/ta/pvo.js +3 -1
  222. package/dist/ta/pvo.js.map +1 -1
  223. package/dist/ta/pvt.d.ts +3 -1
  224. package/dist/ta/pvt.d.ts.map +1 -1
  225. package/dist/ta/pvt.js +3 -1
  226. package/dist/ta/pvt.js.map +1 -1
  227. package/dist/ta/registry.d.ts +7 -1
  228. package/dist/ta/registry.d.ts.map +1 -1
  229. package/dist/ta/registry.js +4 -0
  230. package/dist/ta/registry.js.map +1 -1
  231. package/dist/ta/rsi.d.ts +3 -2
  232. package/dist/ta/rsi.d.ts.map +1 -1
  233. package/dist/ta/rsi.js +3 -2
  234. package/dist/ta/rsi.js.map +1 -1
  235. package/dist/ta/rvi.d.ts +3 -2
  236. package/dist/ta/rvi.d.ts.map +1 -1
  237. package/dist/ta/rvi.js +3 -2
  238. package/dist/ta/rvi.js.map +1 -1
  239. package/dist/ta/sessionVolumeProfile.d.ts.map +1 -1
  240. package/dist/ta/sessionVolumeProfile.js +1 -17
  241. package/dist/ta/sessionVolumeProfile.js.map +1 -1
  242. package/dist/ta/sma.d.ts +6 -3
  243. package/dist/ta/sma.d.ts.map +1 -1
  244. package/dist/ta/sma.js +6 -3
  245. package/dist/ta/sma.js.map +1 -1
  246. package/dist/ta/stdev.d.ts +3 -2
  247. package/dist/ta/stdev.d.ts.map +1 -1
  248. package/dist/ta/stdev.js +3 -2
  249. package/dist/ta/stdev.js.map +1 -1
  250. package/dist/ta/trendStrengthIndex.d.ts +3 -2
  251. package/dist/ta/trendStrengthIndex.d.ts.map +1 -1
  252. package/dist/ta/trendStrengthIndex.js +3 -2
  253. package/dist/ta/trendStrengthIndex.js.map +1 -1
  254. package/dist/ta/trix.d.ts +4 -3
  255. package/dist/ta/trix.d.ts.map +1 -1
  256. package/dist/ta/trix.js +4 -3
  257. package/dist/ta/trix.js.map +1 -1
  258. package/dist/ta/vortex.d.ts +3 -2
  259. package/dist/ta/vortex.d.ts.map +1 -1
  260. package/dist/ta/vortex.js +3 -2
  261. package/dist/ta/vortex.js.map +1 -1
  262. package/dist/time-accessors/civil.d.ts +73 -0
  263. package/dist/time-accessors/civil.d.ts.map +1 -0
  264. package/dist/time-accessors/civil.js +105 -0
  265. package/dist/time-accessors/civil.js.map +1 -0
  266. package/dist/time-accessors/index.d.ts +8 -0
  267. package/dist/time-accessors/index.d.ts.map +1 -0
  268. package/dist/time-accessors/index.js +9 -0
  269. package/dist/time-accessors/index.js.map +1 -0
  270. package/dist/time-accessors/sessionAccessors.d.ts +50 -0
  271. package/dist/time-accessors/sessionAccessors.d.ts.map +1 -0
  272. package/dist/time-accessors/sessionAccessors.js +79 -0
  273. package/dist/time-accessors/sessionAccessors.js.map +1 -0
  274. package/dist/time-accessors/sessionWindow.d.ts +17 -0
  275. package/dist/time-accessors/sessionWindow.d.ts.map +1 -0
  276. package/dist/time-accessors/sessionWindow.js +41 -0
  277. package/dist/time-accessors/sessionWindow.js.map +1 -0
  278. package/dist/time-accessors/timeAccessors.d.ts +54 -0
  279. package/dist/time-accessors/timeAccessors.d.ts.map +1 -0
  280. package/dist/time-accessors/timeAccessors.js +132 -0
  281. package/dist/time-accessors/timeAccessors.js.map +1 -0
  282. package/dist/time-accessors/tzDiagnostic.d.ts +17 -0
  283. package/dist/time-accessors/tzDiagnostic.d.ts.map +1 -0
  284. package/dist/time-accessors/tzDiagnostic.js +34 -0
  285. package/dist/time-accessors/tzDiagnostic.js.map +1 -0
  286. package/dist/time-accessors/tzOffset.d.ts +31 -0
  287. package/dist/time-accessors/tzOffset.d.ts.map +1 -0
  288. package/dist/time-accessors/tzOffset.js +67 -0
  289. package/dist/time-accessors/tzOffset.js.map +1 -0
  290. package/package.json +3 -3
  291. package/dist/ta/lib/applyOffset.d.ts +0 -19
  292. package/dist/ta/lib/applyOffset.d.ts.map +0 -1
  293. package/dist/ta/lib/applyOffset.js +0 -38
  294. package/dist/ta/lib/applyOffset.js.map +0 -1
@@ -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"]}
@@ -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.array` namespace
5
+ * (a `${slotIdPrefix}${slotId}:array` key). Lets the restore router separate
6
+ * array slots from scalar `state.*` (`:state`), `state.series` (`:series`), and
7
+ * `ta.*` (`ta:`) slots, all of which share one `slots` record.
8
+ *
9
+ * @since 1.3
10
+ * @internal
11
+ * @stable
12
+ * @example
13
+ * isArraySlotSnapshotKey("x.chart.ts:1:1:array"); // true
14
+ */
15
+ export declare function isArraySlotSnapshotKey(key: string): boolean;
16
+ /**
17
+ * Serialise the runner's `state.array` slots into JSON-clean snapshot entries
18
+ * keyed by the same `${prefix}${slotId}:array` key the slot store uses. Both
19
+ * rings ride {@link Float64RingBuffer.serialiseSnapshotBuffer}; `capacity` is
20
+ * recorded so restore can detect a script-edited capacity and degrade.
21
+ *
22
+ * @since 1.3
23
+ * @internal
24
+ * @stable
25
+ * @example
26
+ * // const entries = serialiseArraySlots(ctx);
27
+ * const entries = {};
28
+ * void entries;
29
+ */
30
+ export declare function serialiseArraySlots(ctx: RuntimeContext): Readonly<Record<string, JsonValue>>;
31
+ /**
32
+ * Restore `state.array` slots from namespaced snapshot entries into
33
+ * `ctx.arraySlots`, rebuilding each ring at the *persisted* `capacity`. Non-array
34
+ * keys are ignored; a malformed snapshot — or one whose ring shape no longer
35
+ * matches its recorded `capacity` — is skipped so the slot starts fresh (a
36
+ * script-edited `state.array(cap)` literal degrades, it does not throw). The
37
+ * handle identity is recreated on restore (acceptable — same as `state.series`).
38
+ *
39
+ * @since 1.3
40
+ * @internal
41
+ * @stable
42
+ * @example
43
+ * // restoreArraySlots(ctx, snapshot.slots);
44
+ * const restored = true;
45
+ * void restored;
46
+ */
47
+ export declare function restoreArraySlots(ctx: RuntimeContext, slots: Readonly<Record<string, unknown>>): void;
48
+ //# sourceMappingURL=arrayPersistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrayPersistence.d.ts","sourceRoot":"","sources":["../../src/state/arrayPersistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAS9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAM3D;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAW5F;AAaD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,IAAI,CASN"}
@@ -0,0 +1,88 @@
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 { isBufferSnapshot, isInteger, isRecord, restoreBuffer, serialiseBuffer, } from "../bufferSnapshot.js";
4
+ import { restoreArrayStateSlot } from "./arrayStateSlot.js";
5
+ const ARRAY_SLOT_SUFFIX = ":array";
6
+ /**
7
+ * Return whether a snapshot slot key belongs to the `state.array` namespace
8
+ * (a `${slotIdPrefix}${slotId}:array` key). Lets the restore router separate
9
+ * array slots from scalar `state.*` (`:state`), `state.series` (`:series`), and
10
+ * `ta.*` (`ta:`) slots, all of which share one `slots` record.
11
+ *
12
+ * @since 1.3
13
+ * @internal
14
+ * @stable
15
+ * @example
16
+ * isArraySlotSnapshotKey("x.chart.ts:1:1:array"); // true
17
+ */
18
+ export function isArraySlotSnapshotKey(key) {
19
+ return key.endsWith(ARRAY_SLOT_SUFFIX);
20
+ }
21
+ /**
22
+ * Serialise the runner's `state.array` slots into JSON-clean snapshot entries
23
+ * keyed by the same `${prefix}${slotId}:array` key the slot store uses. Both
24
+ * rings ride {@link Float64RingBuffer.serialiseSnapshotBuffer}; `capacity` is
25
+ * recorded so restore can detect a script-edited capacity and degrade.
26
+ *
27
+ * @since 1.3
28
+ * @internal
29
+ * @stable
30
+ * @example
31
+ * // const entries = serialiseArraySlots(ctx);
32
+ * const entries = {};
33
+ * void entries;
34
+ */
35
+ export function serialiseArraySlots(ctx) {
36
+ const out = {};
37
+ for (const [key, slot] of ctx.arraySlots.entries()) {
38
+ out[key] = {
39
+ kind: "state.array",
40
+ capacity: slot.capacity,
41
+ committed: serialiseBuffer(slot.committedRing),
42
+ tentative: serialiseBuffer(slot.tentativeRing),
43
+ };
44
+ }
45
+ return Object.freeze(out);
46
+ }
47
+ function restoreArraySlotSnapshot(snapshot) {
48
+ if (!isRecord(snapshot) || snapshot.kind !== "state.array")
49
+ return null;
50
+ if (!isInteger(snapshot.capacity) || snapshot.capacity <= 0)
51
+ return null;
52
+ const { committed, tentative } = snapshot;
53
+ if (!isBufferSnapshot(committed) || !isBufferSnapshot(tentative))
54
+ return null;
55
+ const committedRing = restoreBuffer(committed, snapshot.capacity);
56
+ const tentativeRing = restoreBuffer(tentative, snapshot.capacity);
57
+ if (committedRing === null || tentativeRing === null)
58
+ return null;
59
+ return restoreArrayStateSlot(committedRing, tentativeRing);
60
+ }
61
+ /**
62
+ * Restore `state.array` slots from namespaced snapshot entries into
63
+ * `ctx.arraySlots`, rebuilding each ring at the *persisted* `capacity`. Non-array
64
+ * keys are ignored; a malformed snapshot — or one whose ring shape no longer
65
+ * matches its recorded `capacity` — is skipped so the slot starts fresh (a
66
+ * script-edited `state.array(cap)` literal degrades, it does not throw). The
67
+ * handle identity is recreated on restore (acceptable — same as `state.series`).
68
+ *
69
+ * @since 1.3
70
+ * @internal
71
+ * @stable
72
+ * @example
73
+ * // restoreArraySlots(ctx, snapshot.slots);
74
+ * const restored = true;
75
+ * void restored;
76
+ */
77
+ export function restoreArraySlots(ctx, slots) {
78
+ ctx.arraySlots.clear();
79
+ for (const [key, value] of Object.entries(slots)) {
80
+ if (!isArraySlotSnapshotKey(key))
81
+ continue;
82
+ const slot = restoreArraySlotSnapshot(value);
83
+ if (slot !== null) {
84
+ ctx.arraySlots.set(key, slot);
85
+ }
86
+ }
87
+ }
88
+ //# sourceMappingURL=arrayPersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrayPersistence.js","sourceRoot":"","sources":["../../src/state/arrayPersistence.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D,OAAO,EACH,gBAAgB,EAChB,SAAS,EACT,QAAQ,EACR,aAAa,EACb,eAAe,GAClB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAC9C,OAAO,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACnD,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,GAAG;YACP,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC;YAC9C,SAAS,EAAE,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC;SACjD,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,wBAAwB,CAAC,QAAiB;IAC/C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;IAC1C,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,MAAM,aAAa,GAAG,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,aAAa,KAAK,IAAI,IAAI,aAAa,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClE,OAAO,qBAAqB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;;GAeG;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,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,SAAS;QAC3C,MAAM,IAAI,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAChB,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,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 isBufferSnapshot,\n isInteger,\n isRecord,\n restoreBuffer,\n serialiseBuffer,\n} from \"../bufferSnapshot.js\";\nimport type { RuntimeContext } from \"../runtimeContext.js\";\nimport type { ArrayStateSlot } from \"./arrayStateSlot.js\";\nimport { restoreArrayStateSlot } from \"./arrayStateSlot.js\";\n\nconst ARRAY_SLOT_SUFFIX = \":array\";\n\n/**\n * Return whether a snapshot slot key belongs to the `state.array` namespace\n * (a `${slotIdPrefix}${slotId}:array` key). Lets the restore router separate\n * array slots from scalar `state.*` (`:state`), `state.series` (`:series`), and\n * `ta.*` (`ta:`) slots, all of which share one `slots` record.\n *\n * @since 1.3\n * @internal\n * @stable\n * @example\n * isArraySlotSnapshotKey(\"x.chart.ts:1:1:array\"); // true\n */\nexport function isArraySlotSnapshotKey(key: string): boolean {\n return key.endsWith(ARRAY_SLOT_SUFFIX);\n}\n\n/**\n * Serialise the runner's `state.array` slots into JSON-clean snapshot entries\n * keyed by the same `${prefix}${slotId}:array` key the slot store uses. Both\n * rings ride {@link Float64RingBuffer.serialiseSnapshotBuffer}; `capacity` is\n * recorded so restore can detect a script-edited capacity and degrade.\n *\n * @since 1.3\n * @internal\n * @stable\n * @example\n * // const entries = serialiseArraySlots(ctx);\n * const entries = {};\n * void entries;\n */\nexport function serialiseArraySlots(ctx: RuntimeContext): Readonly<Record<string, JsonValue>> {\n const out: Record<string, JsonValue> = {};\n for (const [key, slot] of ctx.arraySlots.entries()) {\n out[key] = {\n kind: \"state.array\",\n capacity: slot.capacity,\n committed: serialiseBuffer(slot.committedRing),\n tentative: serialiseBuffer(slot.tentativeRing),\n };\n }\n return Object.freeze(out);\n}\n\nfunction restoreArraySlotSnapshot(snapshot: unknown): ArrayStateSlot | null {\n if (!isRecord(snapshot) || snapshot.kind !== \"state.array\") return null;\n if (!isInteger(snapshot.capacity) || snapshot.capacity <= 0) return null;\n const { committed, tentative } = snapshot;\n if (!isBufferSnapshot(committed) || !isBufferSnapshot(tentative)) return null;\n const committedRing = restoreBuffer(committed, snapshot.capacity);\n const tentativeRing = restoreBuffer(tentative, snapshot.capacity);\n if (committedRing === null || tentativeRing === null) return null;\n return restoreArrayStateSlot(committedRing, tentativeRing);\n}\n\n/**\n * Restore `state.array` slots from namespaced snapshot entries into\n * `ctx.arraySlots`, rebuilding each ring at the *persisted* `capacity`. Non-array\n * keys are ignored; a malformed snapshot — or one whose ring shape no longer\n * matches its recorded `capacity` — is skipped so the slot starts fresh (a\n * script-edited `state.array(cap)` literal degrades, it does not throw). The\n * handle identity is recreated on restore (acceptable — same as `state.series`).\n *\n * @since 1.3\n * @internal\n * @stable\n * @example\n * // restoreArraySlots(ctx, snapshot.slots);\n * const restored = true;\n * void restored;\n */\nexport function restoreArraySlots(\n ctx: RuntimeContext,\n slots: Readonly<Record<string, unknown>>,\n): void {\n ctx.arraySlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n if (!isArraySlotSnapshotKey(key)) continue;\n const slot = restoreArraySlotSnapshot(value);\n if (slot !== null) {\n ctx.arraySlots.set(key, slot);\n }\n }\n}\n"]}
@@ -0,0 +1,78 @@
1
+ import type { MutableArraySlot } from "@invinite-org/chartlang-core";
2
+ import { Float64RingBuffer } from "../ringBuffer.js";
3
+ /**
4
+ * Runtime slot behind a script-facing `state.array(capacity)` handle. Unlike
5
+ * the scalar `StateSlot` (one committed/tentative *value*) or the `SeriesSlot`
6
+ * (one ring advanced once per bar), this is a **bounded FIFO collection** with
7
+ * two `Float64RingBuffer`s: `tentativeRing` holds the live, author-facing
8
+ * pushes; `committedRing` is the bar-close snapshot a tick rolls back to.
9
+ *
10
+ * The committed/tentative discipline mirrors `StateSlot`: pushes during a tick
11
+ * mutate the tentative ring; a head-bar-replacing tick resets it from committed
12
+ * (in-progress pushes discarded); a bar close commits tentative into committed.
13
+ * The two-ring copy is `O(capacity)` per tick via a typed-array
14
+ * {@link Float64RingBuffer.copyFrom} memcpy — bounded because `capacity` is a
15
+ * required compile-time literal (see `tasks/future/state-array/README.md`
16
+ * Architecture Decisions: "Tick rollback = a two-ring buffer copy").
17
+ *
18
+ * @since 1.3
19
+ * @stable
20
+ * @example
21
+ * const slot = new ArrayStateSlot(4);
22
+ * slot.handle.push(1);
23
+ * slot.onBarClose();
24
+ * slot.handle.get(0); // 1
25
+ */
26
+ export declare class ArrayStateSlot {
27
+ readonly capacity: number;
28
+ readonly committedRing: Float64RingBuffer;
29
+ readonly tentativeRing: Float64RingBuffer;
30
+ readonly handle: MutableArraySlot<number>;
31
+ constructor(capacity: number);
32
+ /** Commit the tentative ring into the committed ring (bar close). */
33
+ onBarClose(): void;
34
+ /** Roll the tentative ring back to the committed ring (head-replacing tick). */
35
+ onBarTick(): void;
36
+ }
37
+ /**
38
+ * Build the identity-stable {@link MutableArraySlot} handle over an
39
+ * {@link ArrayStateSlot}. All author-facing reads and writes route through the
40
+ * **tentative** ring (mirroring `StateSlot.set`/`StateSlot.get`, which read and
41
+ * write `tentative` for non-tick slots); the committed ring is the rollback
42
+ * source. `get(out-of-range)` returns `NaN` (the ring's `at` contract), never
43
+ * throws. A plain object with getters — no `Proxy` — because the handle has a
44
+ * fixed method set and is deliberately not number-coercible.
45
+ *
46
+ * @since 1.3
47
+ * @stable
48
+ * @example
49
+ * // const handle = buildArrayHandle(new ArrayStateSlot(4));
50
+ * // handle.push(1);
51
+ * // handle.last(); // 1
52
+ */
53
+ export declare function buildArrayHandle(slot: ArrayStateSlot): MutableArraySlot<number>;
54
+ /**
55
+ * Allocate a fresh {@link ArrayStateSlot} — both rings empty (`size === 0`).
56
+ * Unlike `state.float(init)` there is no seed value: an empty collection starts
57
+ * empty.
58
+ *
59
+ * @since 1.3
60
+ * @stable
61
+ * @example
62
+ * // const slot = createArrayStateSlot(20);
63
+ * // slot.handle.size; // 0
64
+ */
65
+ export declare function createArrayStateSlot(capacity: number): ArrayStateSlot;
66
+ /**
67
+ * Rebuild an {@link ArrayStateSlot} from already-restored rings (snapshot
68
+ * path). The handle identity is recreated — acceptable, same as
69
+ * `state.series` / `ta.*` restore.
70
+ *
71
+ * @since 1.3
72
+ * @stable
73
+ * @example
74
+ * // const slot = restoreArrayStateSlot(committedRing, tentativeRing);
75
+ * // slot.handle.size;
76
+ */
77
+ export declare function restoreArrayStateSlot(committedRing: Float64RingBuffer, tentativeRing: Float64RingBuffer): ArrayStateSlot;
78
+ //# sourceMappingURL=arrayStateSlot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrayStateSlot.d.ts","sourceRoot":"","sources":["../../src/state/arrayStateSlot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,cAAc;aAKK,QAAQ,EAAE,MAAM;IAJ5C,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBAEd,QAAQ,EAAE,MAAM;IAM5C,qEAAqE;IACrE,UAAU,IAAI,IAAI;IAIlB,gFAAgF;IAChF,SAAS,IAAI,IAAI;CAGpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAqB/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAErE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACjC,aAAa,EAAE,iBAAiB,EAChC,aAAa,EAAE,iBAAiB,GACjC,cAAc,CAKhB"}
@@ -0,0 +1,116 @@
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 { Float64RingBuffer } from "../ringBuffer.js";
4
+ /**
5
+ * Runtime slot behind a script-facing `state.array(capacity)` handle. Unlike
6
+ * the scalar `StateSlot` (one committed/tentative *value*) or the `SeriesSlot`
7
+ * (one ring advanced once per bar), this is a **bounded FIFO collection** with
8
+ * two `Float64RingBuffer`s: `tentativeRing` holds the live, author-facing
9
+ * pushes; `committedRing` is the bar-close snapshot a tick rolls back to.
10
+ *
11
+ * The committed/tentative discipline mirrors `StateSlot`: pushes during a tick
12
+ * mutate the tentative ring; a head-bar-replacing tick resets it from committed
13
+ * (in-progress pushes discarded); a bar close commits tentative into committed.
14
+ * The two-ring copy is `O(capacity)` per tick via a typed-array
15
+ * {@link Float64RingBuffer.copyFrom} memcpy — bounded because `capacity` is a
16
+ * required compile-time literal (see `tasks/future/state-array/README.md`
17
+ * Architecture Decisions: "Tick rollback = a two-ring buffer copy").
18
+ *
19
+ * @since 1.3
20
+ * @stable
21
+ * @example
22
+ * const slot = new ArrayStateSlot(4);
23
+ * slot.handle.push(1);
24
+ * slot.onBarClose();
25
+ * slot.handle.get(0); // 1
26
+ */
27
+ export class ArrayStateSlot {
28
+ capacity;
29
+ committedRing;
30
+ tentativeRing;
31
+ handle;
32
+ constructor(capacity) {
33
+ this.capacity = capacity;
34
+ this.committedRing = new Float64RingBuffer(capacity);
35
+ this.tentativeRing = new Float64RingBuffer(capacity);
36
+ this.handle = buildArrayHandle(this);
37
+ }
38
+ /** Commit the tentative ring into the committed ring (bar close). */
39
+ onBarClose() {
40
+ this.committedRing.copyFrom(this.tentativeRing);
41
+ }
42
+ /** Roll the tentative ring back to the committed ring (head-replacing tick). */
43
+ onBarTick() {
44
+ this.tentativeRing.copyFrom(this.committedRing);
45
+ }
46
+ }
47
+ /**
48
+ * Build the identity-stable {@link MutableArraySlot} handle over an
49
+ * {@link ArrayStateSlot}. All author-facing reads and writes route through the
50
+ * **tentative** ring (mirroring `StateSlot.set`/`StateSlot.get`, which read and
51
+ * write `tentative` for non-tick slots); the committed ring is the rollback
52
+ * source. `get(out-of-range)` returns `NaN` (the ring's `at` contract), never
53
+ * throws. A plain object with getters — no `Proxy` — because the handle has a
54
+ * fixed method set and is deliberately not number-coercible.
55
+ *
56
+ * @since 1.3
57
+ * @stable
58
+ * @example
59
+ * // const handle = buildArrayHandle(new ArrayStateSlot(4));
60
+ * // handle.push(1);
61
+ * // handle.last(); // 1
62
+ */
63
+ export function buildArrayHandle(slot) {
64
+ return {
65
+ push(value) {
66
+ slot.tentativeRing.append(value);
67
+ },
68
+ get(n) {
69
+ return slot.tentativeRing.at(n);
70
+ },
71
+ last() {
72
+ return slot.tentativeRing.at(0);
73
+ },
74
+ clear() {
75
+ slot.tentativeRing.reset();
76
+ },
77
+ get size() {
78
+ return slot.tentativeRing.length;
79
+ },
80
+ get capacity() {
81
+ return slot.capacity;
82
+ },
83
+ };
84
+ }
85
+ /**
86
+ * Allocate a fresh {@link ArrayStateSlot} — both rings empty (`size === 0`).
87
+ * Unlike `state.float(init)` there is no seed value: an empty collection starts
88
+ * empty.
89
+ *
90
+ * @since 1.3
91
+ * @stable
92
+ * @example
93
+ * // const slot = createArrayStateSlot(20);
94
+ * // slot.handle.size; // 0
95
+ */
96
+ export function createArrayStateSlot(capacity) {
97
+ return new ArrayStateSlot(capacity);
98
+ }
99
+ /**
100
+ * Rebuild an {@link ArrayStateSlot} from already-restored rings (snapshot
101
+ * path). The handle identity is recreated — acceptable, same as
102
+ * `state.series` / `ta.*` restore.
103
+ *
104
+ * @since 1.3
105
+ * @stable
106
+ * @example
107
+ * // const slot = restoreArrayStateSlot(committedRing, tentativeRing);
108
+ * // slot.handle.size;
109
+ */
110
+ export function restoreArrayStateSlot(committedRing, tentativeRing) {
111
+ const slot = new ArrayStateSlot(committedRing.capacity);
112
+ slot.committedRing.copyFrom(committedRing);
113
+ slot.tentativeRing.copyFrom(tentativeRing);
114
+ return slot;
115
+ }
116
+ //# sourceMappingURL=arrayStateSlot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrayStateSlot.js","sourceRoot":"","sources":["../../src/state/arrayStateSlot.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAI/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,cAAc;IAKK;IAJnB,aAAa,CAAoB;IACjC,aAAa,CAAoB;IACjC,MAAM,CAA2B;IAE1C,YAA4B,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;QACxC,IAAI,CAAC,aAAa,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,CAAC,aAAa,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,qEAAqE;IACrE,UAAU;QACN,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACpD,CAAC;IAED,gFAAgF;IAChF,SAAS;QACL,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACpD,CAAC;CACJ;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAoB;IACjD,OAAO;QACH,IAAI,CAAC,KAAa;YACd,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,GAAG,CAAC,CAAS;YACT,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,IAAI;YACA,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,KAAK;YACD,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC;QACD,IAAI,IAAI;YACJ,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;QACrC,CAAC;QACD,IAAI,QAAQ;YACR,OAAO,IAAI,CAAC,QAAQ,CAAC;QACzB,CAAC;KACJ,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACjD,OAAO,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACjC,aAAgC,EAChC,aAAgC;IAEhC,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC3C,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC;AAChB,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 { MutableArraySlot } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\n\n/**\n * Runtime slot behind a script-facing `state.array(capacity)` handle. Unlike\n * the scalar `StateSlot` (one committed/tentative *value*) or the `SeriesSlot`\n * (one ring advanced once per bar), this is a **bounded FIFO collection** with\n * two `Float64RingBuffer`s: `tentativeRing` holds the live, author-facing\n * pushes; `committedRing` is the bar-close snapshot a tick rolls back to.\n *\n * The committed/tentative discipline mirrors `StateSlot`: pushes during a tick\n * mutate the tentative ring; a head-bar-replacing tick resets it from committed\n * (in-progress pushes discarded); a bar close commits tentative into committed.\n * The two-ring copy is `O(capacity)` per tick via a typed-array\n * {@link Float64RingBuffer.copyFrom} memcpy — bounded because `capacity` is a\n * required compile-time literal (see `tasks/future/state-array/README.md`\n * Architecture Decisions: \"Tick rollback = a two-ring buffer copy\").\n *\n * @since 1.3\n * @stable\n * @example\n * const slot = new ArrayStateSlot(4);\n * slot.handle.push(1);\n * slot.onBarClose();\n * slot.handle.get(0); // 1\n */\nexport class ArrayStateSlot {\n readonly committedRing: Float64RingBuffer;\n readonly tentativeRing: Float64RingBuffer;\n readonly handle: MutableArraySlot<number>;\n\n constructor(public readonly capacity: number) {\n this.committedRing = new Float64RingBuffer(capacity);\n this.tentativeRing = new Float64RingBuffer(capacity);\n this.handle = buildArrayHandle(this);\n }\n\n /** Commit the tentative ring into the committed ring (bar close). */\n onBarClose(): void {\n this.committedRing.copyFrom(this.tentativeRing);\n }\n\n /** Roll the tentative ring back to the committed ring (head-replacing tick). */\n onBarTick(): void {\n this.tentativeRing.copyFrom(this.committedRing);\n }\n}\n\n/**\n * Build the identity-stable {@link MutableArraySlot} handle over an\n * {@link ArrayStateSlot}. All author-facing reads and writes route through the\n * **tentative** ring (mirroring `StateSlot.set`/`StateSlot.get`, which read and\n * write `tentative` for non-tick slots); the committed ring is the rollback\n * source. `get(out-of-range)` returns `NaN` (the ring's `at` contract), never\n * throws. A plain object with getters — no `Proxy` — because the handle has a\n * fixed method set and is deliberately not number-coercible.\n *\n * @since 1.3\n * @stable\n * @example\n * // const handle = buildArrayHandle(new ArrayStateSlot(4));\n * // handle.push(1);\n * // handle.last(); // 1\n */\nexport function buildArrayHandle(slot: ArrayStateSlot): MutableArraySlot<number> {\n return {\n push(value: number): void {\n slot.tentativeRing.append(value);\n },\n get(n: number): number {\n return slot.tentativeRing.at(n);\n },\n last(): number {\n return slot.tentativeRing.at(0);\n },\n clear(): void {\n slot.tentativeRing.reset();\n },\n get size(): number {\n return slot.tentativeRing.length;\n },\n get capacity(): number {\n return slot.capacity;\n },\n };\n}\n\n/**\n * Allocate a fresh {@link ArrayStateSlot} — both rings empty (`size === 0`).\n * Unlike `state.float(init)` there is no seed value: an empty collection starts\n * empty.\n *\n * @since 1.3\n * @stable\n * @example\n * // const slot = createArrayStateSlot(20);\n * // slot.handle.size; // 0\n */\nexport function createArrayStateSlot(capacity: number): ArrayStateSlot {\n return new ArrayStateSlot(capacity);\n}\n\n/**\n * Rebuild an {@link ArrayStateSlot} from already-restored rings (snapshot\n * path). The handle identity is recreated — acceptable, same as\n * `state.series` / `ta.*` restore.\n *\n * @since 1.3\n * @stable\n * @example\n * // const slot = restoreArrayStateSlot(committedRing, tentativeRing);\n * // slot.handle.size;\n */\nexport function restoreArrayStateSlot(\n committedRing: Float64RingBuffer,\n tentativeRing: Float64RingBuffer,\n): ArrayStateSlot {\n const slot = new ArrayStateSlot(committedRing.capacity);\n slot.committedRing.copyFrom(committedRing);\n slot.tentativeRing.copyFrom(tentativeRing);\n return slot;\n}\n"]}
@@ -1,5 +1,8 @@
1
- export { commitStateSlots, flushStateSlots, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
1
+ export { isArraySlotSnapshotKey, restoreArraySlots, serialiseArraySlots, } from "./arrayPersistence.js";
2
+ export { ArrayStateSlot } from "./arrayStateSlot.js";
3
+ export { advanceSeriesSlots, commitArraySlots, commitSeriesSlots, commitStateSlots, flushStateSlots, resetSeriesHeads, resetTentativeArraySlots, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
2
4
  export type { StateSlotSnapshot } from "./lifecycle.js";
5
+ export { isSeriesSlotSnapshotKey, restoreSeriesSlots, serialiseSeriesSlots, } from "./seriesPersistence.js";
3
6
  export { buildStateNamespace } from "./stateNamespace.js";
4
7
  export type { StateSlotSerialisers } from "./stateSlot.js";
5
8
  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,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACH,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,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,9 @@
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 { isArraySlotSnapshotKey, restoreArraySlots, serialiseArraySlots, } from "./arrayPersistence.js";
4
+ export { ArrayStateSlot } from "./arrayStateSlot.js";
5
+ export { advanceSeriesSlots, commitArraySlots, commitSeriesSlots, commitStateSlots, flushStateSlots, resetSeriesHeads, resetTentativeArraySlots, restoreStateSlots, resetTentativeStateSlots, serialiseStateSlots, } from "./lifecycle.js";
6
+ export { isSeriesSlotSnapshotKey, restoreSeriesSlots, serialiseSeriesSlots, } from "./seriesPersistence.js";
4
7
  export { buildStateNamespace } from "./stateNamespace.js";
5
8
  export { asMutableSlot, StateSlot } from "./stateSlot.js";
6
9
  //# 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,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACH,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,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 isArraySlotSnapshotKey,\n restoreArraySlots,\n serialiseArraySlots,\n} from \"./arrayPersistence.js\";\nexport { ArrayStateSlot } from \"./arrayStateSlot.js\";\nexport {\n advanceSeriesSlots,\n commitArraySlots,\n commitSeriesSlots,\n commitStateSlots,\n flushStateSlots,\n resetSeriesHeads,\n resetTentativeArraySlots,\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,72 @@ 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;
115
+ /**
116
+ * Roll every `state.array` slot's tentative ring back to its committed ring
117
+ * before tick compute, so a head-replacing tick discards in-progress pushes
118
+ * (and a tick without a push reads the committed collection). Runs once per
119
+ * tick, before compute, next to {@link resetSeriesHeads}. There is no advance —
120
+ * the array changes only when the author pushes.
121
+ *
122
+ * @since 1.3
123
+ * @stable
124
+ * @example
125
+ * // resetTentativeArraySlots(ctx);
126
+ * const reset = true;
127
+ * void reset;
128
+ */
129
+ export declare function resetTentativeArraySlots(ctx: RuntimeContext): void;
130
+ /**
131
+ * Commit every `state.array` slot's tentative ring into its committed ring
132
+ * after close compute, so the next tick can roll back to it. Runs once per
133
+ * close, after compute, next to {@link commitSeriesSlots}.
134
+ *
135
+ * @since 1.3
136
+ * @stable
137
+ * @example
138
+ * // commitArraySlots(ctx);
139
+ * const committed = true;
140
+ * void committed;
141
+ */
142
+ export declare function commitArraySlots(ctx: RuntimeContext): void;
75
143
  //# 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;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAIlE;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,92 @@ 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
+ }
140
+ /**
141
+ * Roll every `state.array` slot's tentative ring back to its committed ring
142
+ * before tick compute, so a head-replacing tick discards in-progress pushes
143
+ * (and a tick without a push reads the committed collection). Runs once per
144
+ * tick, before compute, next to {@link resetSeriesHeads}. There is no advance —
145
+ * the array changes only when the author pushes.
146
+ *
147
+ * @since 1.3
148
+ * @stable
149
+ * @example
150
+ * // resetTentativeArraySlots(ctx);
151
+ * const reset = true;
152
+ * void reset;
153
+ */
154
+ export function resetTentativeArraySlots(ctx) {
155
+ for (const slot of ctx.arraySlots.values()) {
156
+ slot.onBarTick();
157
+ }
158
+ }
159
+ /**
160
+ * Commit every `state.array` slot's tentative ring into its committed ring
161
+ * after close compute, so the next tick can roll back to it. Runs once per
162
+ * close, after compute, next to {@link commitSeriesSlots}.
163
+ *
164
+ * @since 1.3
165
+ * @stable
166
+ * @example
167
+ * // commitArraySlots(ctx);
168
+ * const committed = true;
169
+ * void committed;
170
+ */
171
+ export function commitArraySlots(ctx) {
172
+ for (const slot of ctx.arraySlots.values()) {
173
+ slot.onBarClose();
174
+ }
175
+ }
87
176
  //# sourceMappingURL=lifecycle.js.map