@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":"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":"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","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\n/**\n * Roll every `state.array` slot's tentative ring back to its committed ring\n * before tick compute, so a head-replacing tick discards in-progress pushes\n * (and a tick without a push reads the committed collection). Runs once per\n * tick, before compute, next to {@link resetSeriesHeads}. There is no advance —\n * the array changes only when the author pushes.\n *\n * @since 1.3\n * @stable\n * @example\n * // resetTentativeArraySlots(ctx);\n * const reset = true;\n * void reset;\n */\nexport function resetTentativeArraySlots(ctx: RuntimeContext): void {\n for (const slot of ctx.arraySlots.values()) {\n slot.onBarTick();\n }\n}\n\n/**\n * Commit every `state.array` slot's tentative ring into its committed ring\n * after close compute, so the next tick can roll back to it. Runs once per\n * close, after compute, next to {@link commitSeriesSlots}.\n *\n * @since 1.3\n * @stable\n * @example\n * // commitArraySlots(ctx);\n * const committed = true;\n * void committed;\n */\nexport function commitArraySlots(ctx: RuntimeContext): void {\n for (const slot of ctx.arraySlots.values()) {\n slot.onBarClose();\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"]}
@@ -0,0 +1,105 @@
1
+ import type { NumberSeriesSlot } from "@invinite-org/chartlang-core";
2
+ import type { Float64RingBuffer } from "../ringBuffer.js";
3
+ /**
4
+ * Runtime slot behind a script-facing `state.series(init)` handle. The
5
+ * `buffer` is the history ring (index 0 = live head); the `view` is the
6
+ * identity-stable {@link NumberSeriesSlot} the script reads and writes;
7
+ * `committedHead` snapshots the head as of the last bar close so a tick
8
+ * can reset the live head before the script refines it.
9
+ *
10
+ * Unlike the scalar `StateSlot`, there is no tentative/committed value
11
+ * split — the head IS the tentative value and history IS committed (a bar
12
+ * advances the ring on close). `committedHead` exists only so a tick's
13
+ * `resetSeriesSlotHead` can undo a prior tick's `replaceHead`.
14
+ *
15
+ * @since 0.9
16
+ * @stable
17
+ * @example
18
+ * // const slot = createSeriesSlot(new Float64RingBuffer(8), 0);
19
+ * // slot.view.value = 42;
20
+ * // slot.view[0]; // 42
21
+ */
22
+ export type SeriesSlot = {
23
+ readonly kind: "state.series";
24
+ readonly buffer: Float64RingBuffer;
25
+ readonly view: NumberSeriesSlot;
26
+ committedHead: number;
27
+ };
28
+ /**
29
+ * Build the identity-stable {@link NumberSeriesSlot} view over a ring
30
+ * buffer. Series reads (`[n]`, `current`, `length`, `valueOf`,
31
+ * `Symbol.toPrimitive`) delegate to a reused {@link makeSeriesView}; the
32
+ * `value` property is added on top — `get` → `buffer.at(0)` (live head),
33
+ * `set` → `buffer.replaceHead(v)` (write-through to the live head). The
34
+ * object's identity is stable across bars, so a script can keep
35
+ * `const s = state.series(0)` at the top of `compute`.
36
+ *
37
+ * @since 0.9
38
+ * @stable
39
+ * @example
40
+ * // const view = makeSeriesSlotView(buffer);
41
+ * // view.value = 7; // replaceHead
42
+ * // +view; // 7 (valueOf → buffer.at(0))
43
+ * // view[1]; // one committed bar back
44
+ */
45
+ export declare function makeSeriesSlotView(buffer: Float64RingBuffer): NumberSeriesSlot;
46
+ /**
47
+ * Allocate a fresh {@link SeriesSlot}: seed the live head with `init`
48
+ * (matching `state.float(init)` — a never-written series reads `init` on
49
+ * its allocation bar), set `committedHead = init`, and build the
50
+ * identity-stable view. Later bars the script does not write become `NaN`
51
+ * gaps because the close hook advances the ring with `append(NaN)`.
52
+ *
53
+ * @since 0.9
54
+ * @stable
55
+ * @example
56
+ * // const slot = createSeriesSlot(new Float64RingBuffer(8), 0);
57
+ * // slot.view[0]; // 0 (the seeded init on the allocation bar)
58
+ */
59
+ export declare function createSeriesSlot(buffer: Float64RingBuffer, init: number): SeriesSlot;
60
+ /**
61
+ * Rebuild a {@link SeriesSlot} over an already-restored ring buffer
62
+ * (snapshot path). The view identity is recreated — acceptable, same as
63
+ * `ta.*` restore.
64
+ *
65
+ * @since 0.9
66
+ * @stable
67
+ * @example
68
+ * // const slot = restoreSeriesSlot(restoredBuffer, committedHead);
69
+ * // slot.view[1];
70
+ */
71
+ export declare function restoreSeriesSlot(buffer: Float64RingBuffer, committedHead: number): SeriesSlot;
72
+ /**
73
+ * Advance the ring for a new close bar: append a fresh `NaN` head so the
74
+ * prior committed head slides to index 1. Runs once per close, before the
75
+ * script's compute, for every already-allocated slot.
76
+ *
77
+ * @since 0.9
78
+ * @stable
79
+ * @example
80
+ * // advanceSeriesSlot(slot); // slot.view[0] is now NaN until written
81
+ */
82
+ export declare function advanceSeriesSlot(slot: SeriesSlot): void;
83
+ /**
84
+ * Commit the live head as the bar-close value, so the next close's
85
+ * `advanceSeriesSlot` retains it and a subsequent tick's
86
+ * `resetSeriesSlotHead` restores it. Runs once per close, after compute.
87
+ *
88
+ * @since 0.9
89
+ * @stable
90
+ * @example
91
+ * // commitSeriesSlot(slot); // slot.committedHead = slot.view[0]
92
+ */
93
+ export declare function commitSeriesSlot(slot: SeriesSlot): void;
94
+ /**
95
+ * Reset the live head to the last committed value at the start of a tick,
96
+ * so a tick that re-writes refines from the committed baseline and a tick
97
+ * that does not write reads the committed head. Does NOT advance length.
98
+ *
99
+ * @since 0.9
100
+ * @stable
101
+ * @example
102
+ * // resetSeriesSlotHead(slot); // slot.view[0] = slot.committedHead
103
+ */
104
+ export declare function resetSeriesSlotHead(slot: SeriesSlot): void;
105
+ //# sourceMappingURL=seriesSlot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seriesSlot.d.ts","sourceRoot":"","sources":["../../src/state/seriesSlot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,UAAU,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,gBAAgB,CAmB9E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAQpF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAO9F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAE1D"}
@@ -0,0 +1,123 @@
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 { makeSeriesView } from "../seriesView.js";
4
+ /**
5
+ * Build the identity-stable {@link NumberSeriesSlot} view over a ring
6
+ * buffer. Series reads (`[n]`, `current`, `length`, `valueOf`,
7
+ * `Symbol.toPrimitive`) delegate to a reused {@link makeSeriesView}; the
8
+ * `value` property is added on top — `get` → `buffer.at(0)` (live head),
9
+ * `set` → `buffer.replaceHead(v)` (write-through to the live head). The
10
+ * object's identity is stable across bars, so a script can keep
11
+ * `const s = state.series(0)` at the top of `compute`.
12
+ *
13
+ * @since 0.9
14
+ * @stable
15
+ * @example
16
+ * // const view = makeSeriesSlotView(buffer);
17
+ * // view.value = 7; // replaceHead
18
+ * // +view; // 7 (valueOf → buffer.at(0))
19
+ * // view[1]; // one committed bar back
20
+ */
21
+ export function makeSeriesSlotView(buffer) {
22
+ const reads = makeSeriesView(buffer);
23
+ return new Proxy(reads, {
24
+ get(target, prop, receiver) {
25
+ if (prop === "value")
26
+ return buffer.at(0);
27
+ return Reflect.get(target, prop, receiver);
28
+ },
29
+ set(_target, prop, value) {
30
+ if (prop === "value") {
31
+ buffer.replaceHead(value);
32
+ return true;
33
+ }
34
+ return false;
35
+ },
36
+ has(target, prop) {
37
+ if (prop === "value")
38
+ return true;
39
+ return Reflect.has(target, prop);
40
+ },
41
+ });
42
+ }
43
+ /**
44
+ * Allocate a fresh {@link SeriesSlot}: seed the live head with `init`
45
+ * (matching `state.float(init)` — a never-written series reads `init` on
46
+ * its allocation bar), set `committedHead = init`, and build the
47
+ * identity-stable view. Later bars the script does not write become `NaN`
48
+ * gaps because the close hook advances the ring with `append(NaN)`.
49
+ *
50
+ * @since 0.9
51
+ * @stable
52
+ * @example
53
+ * // const slot = createSeriesSlot(new Float64RingBuffer(8), 0);
54
+ * // slot.view[0]; // 0 (the seeded init on the allocation bar)
55
+ */
56
+ export function createSeriesSlot(buffer, init) {
57
+ buffer.append(init);
58
+ return {
59
+ kind: "state.series",
60
+ buffer,
61
+ view: makeSeriesSlotView(buffer),
62
+ committedHead: init,
63
+ };
64
+ }
65
+ /**
66
+ * Rebuild a {@link SeriesSlot} over an already-restored ring buffer
67
+ * (snapshot path). The view identity is recreated — acceptable, same as
68
+ * `ta.*` restore.
69
+ *
70
+ * @since 0.9
71
+ * @stable
72
+ * @example
73
+ * // const slot = restoreSeriesSlot(restoredBuffer, committedHead);
74
+ * // slot.view[1];
75
+ */
76
+ export function restoreSeriesSlot(buffer, committedHead) {
77
+ return {
78
+ kind: "state.series",
79
+ buffer,
80
+ view: makeSeriesSlotView(buffer),
81
+ committedHead,
82
+ };
83
+ }
84
+ /**
85
+ * Advance the ring for a new close bar: append a fresh `NaN` head so the
86
+ * prior committed head slides to index 1. Runs once per close, before the
87
+ * script's compute, for every already-allocated slot.
88
+ *
89
+ * @since 0.9
90
+ * @stable
91
+ * @example
92
+ * // advanceSeriesSlot(slot); // slot.view[0] is now NaN until written
93
+ */
94
+ export function advanceSeriesSlot(slot) {
95
+ slot.buffer.append(Number.NaN);
96
+ }
97
+ /**
98
+ * Commit the live head as the bar-close value, so the next close's
99
+ * `advanceSeriesSlot` retains it and a subsequent tick's
100
+ * `resetSeriesSlotHead` restores it. Runs once per close, after compute.
101
+ *
102
+ * @since 0.9
103
+ * @stable
104
+ * @example
105
+ * // commitSeriesSlot(slot); // slot.committedHead = slot.view[0]
106
+ */
107
+ export function commitSeriesSlot(slot) {
108
+ slot.committedHead = slot.buffer.at(0);
109
+ }
110
+ /**
111
+ * Reset the live head to the last committed value at the start of a tick,
112
+ * so a tick that re-writes refines from the committed baseline and a tick
113
+ * that does not write reads the committed head. Does NOT advance length.
114
+ *
115
+ * @since 0.9
116
+ * @stable
117
+ * @example
118
+ * // resetSeriesSlotHead(slot); // slot.view[0] = slot.committedHead
119
+ */
120
+ export function resetSeriesSlotHead(slot) {
121
+ slot.buffer.replaceHead(slot.committedHead);
122
+ }
123
+ //# sourceMappingURL=seriesSlot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seriesSlot.js","sourceRoot":"","sources":["../../src/state/seriesSlot.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAK/D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA4BlD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IACxD,MAAM,KAAK,GAAG,cAAc,CAAS,MAAM,CAAC,CAAC;IAC7C,OAAO,IAAI,KAAK,CAAC,KAAyB,EAAE;QACxC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACtB,IAAI,IAAI,KAAK,OAAO;gBAAE,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1C,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC/C,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK;YACpB,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACnB,MAAM,CAAC,WAAW,CAAC,KAAe,CAAC,CAAC;gBACpC,OAAO,IAAI,CAAC;YAChB,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,IAAI;YACZ,IAAI,IAAI,KAAK,OAAO;gBAAE,OAAO,IAAI,CAAC;YAClC,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;KACJ,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAyB,EAAE,IAAY;IACpE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO;QACH,IAAI,EAAE,cAAc;QACpB,MAAM;QACN,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC;QAChC,aAAa,EAAE,IAAI;KACtB,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAyB,EAAE,aAAqB;IAC9E,OAAO;QACH,IAAI,EAAE,cAAc;QACpB,MAAM;QACN,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC;QAChC,aAAa;KAChB,CAAC;AACN,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAgB;IAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAgB;IAC7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAgB;IAChD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAChD,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 { NumberSeriesSlot } from \"@invinite-org/chartlang-core\";\n\nimport type { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { makeSeriesView } from \"../seriesView.js\";\n\n/**\n * Runtime slot behind a script-facing `state.series(init)` handle. The\n * `buffer` is the history ring (index 0 = live head); the `view` is the\n * identity-stable {@link NumberSeriesSlot} the script reads and writes;\n * `committedHead` snapshots the head as of the last bar close so a tick\n * can reset the live head before the script refines it.\n *\n * Unlike the scalar `StateSlot`, there is no tentative/committed value\n * split — the head IS the tentative value and history IS committed (a bar\n * advances the ring on close). `committedHead` exists only so a tick's\n * `resetSeriesSlotHead` can undo a prior tick's `replaceHead`.\n *\n * @since 0.9\n * @stable\n * @example\n * // const slot = createSeriesSlot(new Float64RingBuffer(8), 0);\n * // slot.view.value = 42;\n * // slot.view[0]; // 42\n */\nexport type SeriesSlot = {\n readonly kind: \"state.series\";\n readonly buffer: Float64RingBuffer;\n readonly view: NumberSeriesSlot;\n committedHead: number;\n};\n\n/**\n * Build the identity-stable {@link NumberSeriesSlot} view over a ring\n * buffer. Series reads (`[n]`, `current`, `length`, `valueOf`,\n * `Symbol.toPrimitive`) delegate to a reused {@link makeSeriesView}; the\n * `value` property is added on top — `get` → `buffer.at(0)` (live head),\n * `set` → `buffer.replaceHead(v)` (write-through to the live head). The\n * object's identity is stable across bars, so a script can keep\n * `const s = state.series(0)` at the top of `compute`.\n *\n * @since 0.9\n * @stable\n * @example\n * // const view = makeSeriesSlotView(buffer);\n * // view.value = 7; // replaceHead\n * // +view; // 7 (valueOf → buffer.at(0))\n * // view[1]; // one committed bar back\n */\nexport function makeSeriesSlotView(buffer: Float64RingBuffer): NumberSeriesSlot {\n const reads = makeSeriesView<number>(buffer);\n return new Proxy(reads as NumberSeriesSlot, {\n get(target, prop, receiver) {\n if (prop === \"value\") return buffer.at(0);\n return Reflect.get(target, prop, receiver);\n },\n set(_target, prop, value) {\n if (prop === \"value\") {\n buffer.replaceHead(value as number);\n return true;\n }\n return false;\n },\n has(target, prop) {\n if (prop === \"value\") return true;\n return Reflect.has(target, prop);\n },\n });\n}\n\n/**\n * Allocate a fresh {@link SeriesSlot}: seed the live head with `init`\n * (matching `state.float(init)` — a never-written series reads `init` on\n * its allocation bar), set `committedHead = init`, and build the\n * identity-stable view. Later bars the script does not write become `NaN`\n * gaps because the close hook advances the ring with `append(NaN)`.\n *\n * @since 0.9\n * @stable\n * @example\n * // const slot = createSeriesSlot(new Float64RingBuffer(8), 0);\n * // slot.view[0]; // 0 (the seeded init on the allocation bar)\n */\nexport function createSeriesSlot(buffer: Float64RingBuffer, init: number): SeriesSlot {\n buffer.append(init);\n return {\n kind: \"state.series\",\n buffer,\n view: makeSeriesSlotView(buffer),\n committedHead: init,\n };\n}\n\n/**\n * Rebuild a {@link SeriesSlot} over an already-restored ring buffer\n * (snapshot path). The view identity is recreated — acceptable, same as\n * `ta.*` restore.\n *\n * @since 0.9\n * @stable\n * @example\n * // const slot = restoreSeriesSlot(restoredBuffer, committedHead);\n * // slot.view[1];\n */\nexport function restoreSeriesSlot(buffer: Float64RingBuffer, committedHead: number): SeriesSlot {\n return {\n kind: \"state.series\",\n buffer,\n view: makeSeriesSlotView(buffer),\n committedHead,\n };\n}\n\n/**\n * Advance the ring for a new close bar: append a fresh `NaN` head so the\n * prior committed head slides to index 1. Runs once per close, before the\n * script's compute, for every already-allocated slot.\n *\n * @since 0.9\n * @stable\n * @example\n * // advanceSeriesSlot(slot); // slot.view[0] is now NaN until written\n */\nexport function advanceSeriesSlot(slot: SeriesSlot): void {\n slot.buffer.append(Number.NaN);\n}\n\n/**\n * Commit the live head as the bar-close value, so the next close's\n * `advanceSeriesSlot` retains it and a subsequent tick's\n * `resetSeriesSlotHead` restores it. Runs once per close, after compute.\n *\n * @since 0.9\n * @stable\n * @example\n * // commitSeriesSlot(slot); // slot.committedHead = slot.view[0]\n */\nexport function commitSeriesSlot(slot: SeriesSlot): void {\n slot.committedHead = slot.buffer.at(0);\n}\n\n/**\n * Reset the live head to the last committed value at the start of a tick,\n * so a tick that re-writes refines from the committed baseline and a tick\n * that does not write reads the committed head. Does NOT advance length.\n *\n * @since 0.9\n * @stable\n * @example\n * // resetSeriesSlotHead(slot); // slot.view[0] = slot.committedHead\n */\nexport function resetSeriesSlotHead(slot: SeriesSlot): void {\n slot.buffer.replaceHead(slot.committedHead);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"stateNamespace.d.ts","sourceRoot":"","sources":["../../src/state/stateNamespace.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAsDhF;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAwBpD"}
1
+ {"version":3,"file":"stateNamespace.d.ts","sourceRoot":"","sources":["../../src/state/stateNamespace.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,cAAc,EACjB,MAAM,8BAA8B,CAAC;AAiHtC;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CA4BpD"}
@@ -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
+ import { Float64RingBuffer } from "../ringBuffer.js";
3
4
  import { ACTIVE_RUNTIME_CONTEXT } from "../runtimeContext.js";
5
+ import { createArrayStateSlot } from "./arrayStateSlot.js";
6
+ import { createSeriesSlot } from "./seriesSlot.js";
4
7
  import { asMutableSlot, StateSlot } from "./stateSlot.js";
5
8
  /**
6
9
  * Compose the runtime's `state.*` slot key from the compiler-injected
@@ -14,6 +17,26 @@ import { asMutableSlot, StateSlot } from "./stateSlot.js";
14
17
  * @internal
15
18
  */
16
19
  const stateKey = (ctx, slotId) => `${ctx.slotIdPrefix ?? ""}${slotId}:state`;
20
+ /**
21
+ * Compose the runtime's `state.series` slot key — the `:series` suffix
22
+ * (vs `:state`) lets the snapshot restore router tell a series slot from a
23
+ * scalar `state.*` slot. The `slotIdPrefix` isolation rule is identical to
24
+ * {@link stateKey}.
25
+ *
26
+ * @since 0.9
27
+ * @internal
28
+ */
29
+ const seriesKey = (ctx, slotId) => `${ctx.slotIdPrefix ?? ""}${slotId}:series`;
30
+ /**
31
+ * Compose the runtime's `state.array` slot key — the `:array` suffix (vs
32
+ * `:state` / `:series`) lets the snapshot restore router tell an array slot
33
+ * from a scalar or series slot. The `slotIdPrefix` isolation rule is identical
34
+ * to {@link stateKey}.
35
+ *
36
+ * @since 1.3
37
+ * @internal
38
+ */
39
+ const arrayKey = (ctx, slotId) => `${ctx.slotIdPrefix ?? ""}${slotId}:array`;
17
40
  function getCtx(name) {
18
41
  const ctx = ACTIVE_RUNTIME_CONTEXT.current;
19
42
  if (ctx === null) {
@@ -36,6 +59,36 @@ function getOrAllocate(name, slotId, init, tickPersistent) {
36
59
  ctx.stateSlots.set(key, slot);
37
60
  return asMutableSlot(slot);
38
61
  }
62
+ function getOrAllocateSeries(slotId, init) {
63
+ const ctx = getCtx("state.series");
64
+ const key = seriesKey(ctx, slotId);
65
+ const existing = ctx.seriesSlots.get(key);
66
+ if (existing !== undefined) {
67
+ return existing.view;
68
+ }
69
+ // Size the ring to the runner's global capacity (`maxLookback + 1`, or
70
+ // the 5000-slot dynamic fallback). Warm restart rehydrates `seriesSlots`
71
+ // up front via `restoreSeriesSlots`, so a restored slot is found above
72
+ // and this seed path only runs for a genuinely first-seen callsite.
73
+ const slot = createSeriesSlot(new Float64RingBuffer(ctx.stream.ohlcv.close.capacity), init);
74
+ ctx.seriesSlots.set(key, slot);
75
+ return slot.view;
76
+ }
77
+ function getOrAllocateArray(slotId, capacity) {
78
+ const ctx = getCtx("state.array");
79
+ const key = arrayKey(ctx, slotId);
80
+ const existing = ctx.arraySlots.get(key);
81
+ if (existing !== undefined) {
82
+ return existing.handle;
83
+ }
84
+ // No store-consult / seed: an empty collection starts empty, and warm
85
+ // restart rehydrates `arraySlots` up front via `restoreArraySlots` (mirrors
86
+ // the `state.series` allocator), so this path only runs for a first-seen
87
+ // callsite.
88
+ const slot = createArrayStateSlot(capacity);
89
+ ctx.arraySlots.set(key, slot);
90
+ return slot.handle;
91
+ }
39
92
  /**
40
93
  * Build the runtime `state` namespace installed on `ComputeContext`.
41
94
  * Each function accepts the compiler-injected `slotId` as its first
@@ -53,6 +106,8 @@ export function buildStateNamespace() {
53
106
  int: (slotId, init) => getOrAllocate("state.int", slotId, init, false),
54
107
  bool: (slotId, init) => getOrAllocate("state.bool", slotId, init, false),
55
108
  string: (slotId, init) => getOrAllocate("state.string", slotId, init, false),
109
+ series: (slotId, init) => getOrAllocateSeries(slotId, init),
110
+ array: (slotId, capacity) => getOrAllocateArray(slotId, capacity),
56
111
  tick: {
57
112
  float: (slotId, init) => getOrAllocate("state.tick.float", slotId, init, true),
58
113
  int: (slotId, init) => getOrAllocate("state.tick.int", slotId, init, true),
@@ -1 +1 @@
1
- {"version":3,"file":"stateNamespace.js","sourceRoot":"","sources":["../../src/state/stateNamespace.ts"],"names":[],"mappings":"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","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 { MutableSlot, StateNamespace } from \"@invinite-org/chartlang-core\";\n\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { asMutableSlot, StateSlot } from \"./stateSlot.js\";\n\ntype StoredStateSlot<T> = {\n readonly committed: T;\n readonly tentative: T;\n};\n\n/**\n * Compose the runtime's `state.*` slot key from the compiler-injected\n * `slotId` plus the active context's `slotIdPrefix`. The primary runner\n * has an absent / empty prefix — its keys stay byte-identical to the\n * Phase-1 `${slotId}:state` shape so single-script snapshots load\n * unchanged. `DepRunner` contexts carry `dep:<localId>/`,\n * `SiblingRunner` contexts carry `export:<exportName>/`.\n *\n * @since 0.7\n * @internal\n */\nconst stateKey = (ctx: RuntimeContext, slotId: string): string =>\n `${ctx.slotIdPrefix ?? \"\"}${slotId}:state`;\n\nfunction getCtx(name: string): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(`${name} called outside an active script step`);\n }\n return ctx;\n}\n\nfunction getOrAllocate<T>(\n name: string,\n slotId: string,\n init: T,\n tickPersistent: boolean,\n): MutableSlot<T> {\n const ctx = getCtx(name);\n const key = stateKey(ctx, slotId);\n const existing = ctx.stateSlots.get(key);\n if (existing !== undefined) {\n return asMutableSlot(existing as StateSlot<T>);\n }\n\n const stored = ctx.stateStore.get<StoredStateSlot<T>>(key);\n const slot = new StateSlot<T>(stored?.committed ?? init, tickPersistent);\n if (stored !== undefined) {\n slot.tentative = stored.tentative;\n }\n ctx.stateSlots.set(key, slot as StateSlot<unknown>);\n return asMutableSlot(slot);\n}\n\n/**\n * Build the runtime `state` namespace installed on `ComputeContext`.\n * Each function accepts the compiler-injected `slotId` as its first\n * parameter, then the script-facing init value.\n *\n * @since 0.4\n * @stable\n * @example\n * const ns = buildStateNamespace();\n * void ns.float;\n */\nexport function buildStateNamespace(): StateNamespace {\n const ns = {\n float: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.float\", slotId, init, false),\n int: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.int\", slotId, init, false),\n bool: (slotId: string, init: boolean): MutableSlot<boolean> =>\n getOrAllocate(\"state.bool\", slotId, init, false),\n string: (slotId: string, init: string): MutableSlot<string> =>\n getOrAllocate(\"state.string\", slotId, init, false),\n tick: {\n float: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.tick.float\", slotId, init, true),\n int: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.tick.int\", slotId, init, true),\n bool: (slotId: string, init: boolean): MutableSlot<boolean> =>\n getOrAllocate(\"state.tick.bool\", slotId, init, true),\n string: (slotId: string, init: string): MutableSlot<string> =>\n getOrAllocate(\"state.tick.string\", slotId, init, true),\n },\n };\n Object.freeze(ns.tick);\n Object.freeze(ns);\n return ns as unknown as StateNamespace;\n}\n"]}
1
+ {"version":3,"file":"stateNamespace.js","sourceRoot":"","sources":["../../src/state/stateNamespace.ts"],"names":[],"mappings":"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","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 {\n MutableArraySlot,\n MutableSlot,\n NumberSeriesSlot,\n StateNamespace,\n} from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { createArrayStateSlot } from \"./arrayStateSlot.js\";\nimport { createSeriesSlot } from \"./seriesSlot.js\";\nimport { asMutableSlot, StateSlot } from \"./stateSlot.js\";\n\ntype StoredStateSlot<T> = {\n readonly committed: T;\n readonly tentative: T;\n};\n\n/**\n * Compose the runtime's `state.*` slot key from the compiler-injected\n * `slotId` plus the active context's `slotIdPrefix`. The primary runner\n * has an absent / empty prefix — its keys stay byte-identical to the\n * Phase-1 `${slotId}:state` shape so single-script snapshots load\n * unchanged. `DepRunner` contexts carry `dep:<localId>/`,\n * `SiblingRunner` contexts carry `export:<exportName>/`.\n *\n * @since 0.7\n * @internal\n */\nconst stateKey = (ctx: RuntimeContext, slotId: string): string =>\n `${ctx.slotIdPrefix ?? \"\"}${slotId}:state`;\n\n/**\n * Compose the runtime's `state.series` slot key — the `:series` suffix\n * (vs `:state`) lets the snapshot restore router tell a series slot from a\n * scalar `state.*` slot. The `slotIdPrefix` isolation rule is identical to\n * {@link stateKey}.\n *\n * @since 0.9\n * @internal\n */\nconst seriesKey = (ctx: RuntimeContext, slotId: string): string =>\n `${ctx.slotIdPrefix ?? \"\"}${slotId}:series`;\n\n/**\n * Compose the runtime's `state.array` slot key — the `:array` suffix (vs\n * `:state` / `:series`) lets the snapshot restore router tell an array slot\n * from a scalar or series slot. The `slotIdPrefix` isolation rule is identical\n * to {@link stateKey}.\n *\n * @since 1.3\n * @internal\n */\nconst arrayKey = (ctx: RuntimeContext, slotId: string): string =>\n `${ctx.slotIdPrefix ?? \"\"}${slotId}:array`;\n\nfunction getCtx(name: string): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(`${name} called outside an active script step`);\n }\n return ctx;\n}\n\nfunction getOrAllocate<T>(\n name: string,\n slotId: string,\n init: T,\n tickPersistent: boolean,\n): MutableSlot<T> {\n const ctx = getCtx(name);\n const key = stateKey(ctx, slotId);\n const existing = ctx.stateSlots.get(key);\n if (existing !== undefined) {\n return asMutableSlot(existing as StateSlot<T>);\n }\n\n const stored = ctx.stateStore.get<StoredStateSlot<T>>(key);\n const slot = new StateSlot<T>(stored?.committed ?? init, tickPersistent);\n if (stored !== undefined) {\n slot.tentative = stored.tentative;\n }\n ctx.stateSlots.set(key, slot as StateSlot<unknown>);\n return asMutableSlot(slot);\n}\n\nfunction getOrAllocateSeries(slotId: string, init: number): NumberSeriesSlot {\n const ctx = getCtx(\"state.series\");\n const key = seriesKey(ctx, slotId);\n const existing = ctx.seriesSlots.get(key);\n if (existing !== undefined) {\n return existing.view;\n }\n // Size the ring to the runner's global capacity (`maxLookback + 1`, or\n // the 5000-slot dynamic fallback). Warm restart rehydrates `seriesSlots`\n // up front via `restoreSeriesSlots`, so a restored slot is found above\n // and this seed path only runs for a genuinely first-seen callsite.\n const slot = createSeriesSlot(new Float64RingBuffer(ctx.stream.ohlcv.close.capacity), init);\n ctx.seriesSlots.set(key, slot);\n return slot.view;\n}\n\nfunction getOrAllocateArray(slotId: string, capacity: number): MutableArraySlot<number> {\n const ctx = getCtx(\"state.array\");\n const key = arrayKey(ctx, slotId);\n const existing = ctx.arraySlots.get(key);\n if (existing !== undefined) {\n return existing.handle;\n }\n // No store-consult / seed: an empty collection starts empty, and warm\n // restart rehydrates `arraySlots` up front via `restoreArraySlots` (mirrors\n // the `state.series` allocator), so this path only runs for a first-seen\n // callsite.\n const slot = createArrayStateSlot(capacity);\n ctx.arraySlots.set(key, slot);\n return slot.handle;\n}\n\n/**\n * Build the runtime `state` namespace installed on `ComputeContext`.\n * Each function accepts the compiler-injected `slotId` as its first\n * parameter, then the script-facing init value.\n *\n * @since 0.4\n * @stable\n * @example\n * const ns = buildStateNamespace();\n * void ns.float;\n */\nexport function buildStateNamespace(): StateNamespace {\n const ns = {\n float: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.float\", slotId, init, false),\n int: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.int\", slotId, init, false),\n bool: (slotId: string, init: boolean): MutableSlot<boolean> =>\n getOrAllocate(\"state.bool\", slotId, init, false),\n string: (slotId: string, init: string): MutableSlot<string> =>\n getOrAllocate(\"state.string\", slotId, init, false),\n series: (slotId: string, init: number): NumberSeriesSlot =>\n getOrAllocateSeries(slotId, init),\n array: (slotId: string, capacity: number): MutableArraySlot<number> =>\n getOrAllocateArray(slotId, capacity),\n tick: {\n float: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.tick.float\", slotId, init, true),\n int: (slotId: string, init: number): MutableSlot<number> =>\n getOrAllocate(\"state.tick.int\", slotId, init, true),\n bool: (slotId: string, init: boolean): MutableSlot<boolean> =>\n getOrAllocate(\"state.tick.bool\", slotId, init, true),\n string: (slotId: string, init: string): MutableSlot<string> =>\n getOrAllocate(\"state.tick.string\", slotId, init, true),\n },\n };\n Object.freeze(ns.tick);\n Object.freeze(ns);\n return ns as unknown as StateNamespace;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { Bar, BarViewport, Series, StreamSnapshot } from "@invinite-org/chartlang-core";
1
+ import type { Bar, BarViewport, Price, PriceSeries, Series, StreamSnapshot, VolumeSeries, WorldPoint } from "@invinite-org/chartlang-core";
2
2
  import { Float64RingBuffer } from "./ringBuffer.js";
3
3
  /**
4
4
  * The per-stream OHLCV ring-buffer set. Each field is a
@@ -31,13 +31,17 @@ export type OhlcvBuffers = {
31
31
  readonly hlcc4: Float64RingBuffer;
32
32
  };
33
33
  /**
34
- * Mutable scalar view of the current bar. Identity stays stable across
35
- * the run Task 6's execution loop mutates the fields in place per
36
- * bar so scripts that destructure `bar` in `compute` keep seeing fresh
37
- * values without rebinding.
34
+ * View of the current bar handed to `compute`. Identity stays stable across
35
+ * the run. The OHLCV + derived price/volume fields are the stream's cached
36
+ * number-coercible `Series` views (one identity per ring buffer) they read
37
+ * the live buffer head, so a script can both use `bar.close` as a scalar
38
+ * (`bar.close * 2`) and index it (`bar.close[1]`) without the runtime copying
39
+ * scalars per bar. `time` stays a mutable scalar (the timestamp axis the
40
+ * emit/draw pipeline consumes as a raw number); `symbol` / `interval` are
41
+ * constant strings for a given `StreamState`.
38
42
  *
39
- * `symbol` and `interval` are constant for a given `StreamState`
40
- * instance; the rest are NaN / 0 before the first bar lands.
43
+ * Before the first bar the buffers are empty, so `bar.close[0]` / `+bar.close`
44
+ * read `NaN` and `bar.close.length` is `0`.
41
45
  *
42
46
  * @since 0.1
43
47
  * @example
@@ -47,24 +51,26 @@ export type OhlcvBuffers = {
47
51
  * // capacity: 5,
48
52
  * // symbol: "AAPL",
49
53
  * // });
50
- * // bar.symbol; // "AAPL"
51
- * // bar.interval; // "1D"
52
- * // bar.close; // NaN until the first bar
54
+ * // bar.symbol; // "AAPL"
55
+ * // bar.interval; // "1D"
56
+ * // +bar.close; // NaN until the first bar
57
+ * // bar.close.length; // 0 until the first bar
53
58
  */
54
59
  export type BarView = {
55
60
  time: number;
56
- open: number;
57
- high: number;
58
- low: number;
59
- close: number;
60
- volume: number;
61
- hl2: number;
62
- hlc3: number;
63
- ohlc4: number;
64
- hlcc4: number;
61
+ open: PriceSeries;
62
+ high: PriceSeries;
63
+ low: PriceSeries;
64
+ close: PriceSeries;
65
+ volume: VolumeSeries;
66
+ hl2: PriceSeries;
67
+ hlc3: PriceSeries;
68
+ ohlc4: PriceSeries;
69
+ hlcc4: PriceSeries;
65
70
  symbol: string;
66
71
  interval: string;
67
72
  viewport: BarViewport;
73
+ point(offset: number, price: Price): WorldPoint;
68
74
  };
69
75
  /**
70
76
  * Everything the runtime owns for a single interval stream — the OHLCV