@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
@@ -0,0 +1,105 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ //
4
+ // Civil-date arithmetic follows Howard Hinnant's public-domain
5
+ // `civil_from_days` / `days_from_civil` algorithm
6
+ // (http://howardhinnant.github.io/date_algorithms.html). Algorithm only —
7
+ // the integer routines are reimplemented in chartlang style, not transcribed.
8
+ const DAY_MS = 86_400_000;
9
+ /**
10
+ * Integer floor-division rounding toward −∞ (unlike JS `/ | 0`, which
11
+ * truncates toward `0`). Required so pre-1970 epochs split correctly.
12
+ *
13
+ * @since 1.5
14
+ * @stable
15
+ * @example
16
+ * // import { floorDiv } from "@invinite-org/chartlang-runtime";
17
+ * // floorDiv(-1, 86_400_000); // -1, not 0
18
+ */
19
+ export function floorDiv(a, b) {
20
+ return Math.floor(a / b);
21
+ }
22
+ /**
23
+ * Non-negative modulo (Euclidean) — `mod(-1, 7) === 6`, unlike JS `%`
24
+ * which would yield `-1`. Pairs with {@link floorDiv} for correct
25
+ * pre-epoch date math.
26
+ *
27
+ * @since 1.5
28
+ * @stable
29
+ * @example
30
+ * // import { mod } from "@invinite-org/chartlang-runtime";
31
+ * // mod(-1, 7); // 6
32
+ */
33
+ export function mod(a, b) {
34
+ return a - floorDiv(a, b) * b;
35
+ }
36
+ /**
37
+ * The civil date for a count of days since the Unix epoch
38
+ * (1970-01-01). `m` is `1..12`, `d` is `1..31`. Pure integer
39
+ * arithmetic — correct for negative (pre-epoch) `z`.
40
+ *
41
+ * @since 1.5
42
+ * @stable
43
+ * @example
44
+ * // import { civilFromDays } from "@invinite-org/chartlang-runtime";
45
+ * // civilFromDays(0); // { y: 1970, m: 1, d: 1 }
46
+ */
47
+ export function civilFromDays(z) {
48
+ const shifted = z + 719_468;
49
+ const era = floorDiv(shifted >= 0 ? shifted : shifted - 146_096, 146_097);
50
+ const doe = shifted - era * 146_097; // [0, 146096]
51
+ const yoe = floorDiv(doe - floorDiv(doe, 1460) + floorDiv(doe, 36_524) - floorDiv(doe, 146_096), 365); // [0, 399]
52
+ const y = yoe + era * 400;
53
+ const doy = doe - (365 * yoe + floorDiv(yoe, 4) - floorDiv(yoe, 100)); // [0, 365]
54
+ const mp = floorDiv(5 * doy + 2, 153); // [0, 11]
55
+ const d = doy - floorDiv(153 * mp + 2, 5) + 1; // [1, 31]
56
+ const m = mp < 10 ? mp + 3 : mp - 9; // [1, 12]
57
+ return { y: m <= 2 ? y + 1 : y, m, d };
58
+ }
59
+ /**
60
+ * Days since the Unix epoch (1970-01-01) for a civil `y` / `m` (`1..12`)
61
+ * / `d` (`1..31`). Inverse of {@link civilFromDays}. Pure integer
62
+ * arithmetic — correct for pre-epoch dates.
63
+ *
64
+ * @since 1.5
65
+ * @stable
66
+ * @example
67
+ * // import { daysFromCivil } from "@invinite-org/chartlang-runtime";
68
+ * // daysFromCivil(1970, 1, 1); // 0
69
+ */
70
+ export function daysFromCivil(y, m, d) {
71
+ const yy = m <= 2 ? y - 1 : y;
72
+ const era = floorDiv(yy >= 0 ? yy : yy - 399, 400);
73
+ const yoe = yy - era * 400; // [0, 399]
74
+ const doy = floorDiv(153 * (m > 2 ? m - 3 : m + 9) + 2, 5) + d - 1; // [0, 365]
75
+ const doe = yoe * 365 + floorDiv(yoe, 4) - floorDiv(yoe, 100) + doy; // [0, 146096]
76
+ return era * 146_097 + doe - 719_468;
77
+ }
78
+ /**
79
+ * Split a UTC ms epoch (after pre-shifting by `offsetMin` minutes) into
80
+ * civil fields. `dow` is `0=Sun .. 6=Sat`, derived from the day count
81
+ * (`mod(z + 4, 7)` — 1970-01-01 was a Thursday, index 4). All integer
82
+ * arithmetic on the floored ms — no `Date`.
83
+ *
84
+ * @since 1.5
85
+ * @stable
86
+ * @example
87
+ * // import { splitEpoch } from "@invinite-org/chartlang-runtime";
88
+ * // splitEpoch(0, 0); // { y: 1970, m: 1, d: 1, hh: 0, mm: 0, ss: 0, dow: 4 }
89
+ */
90
+ export function splitEpoch(ms, offsetMin) {
91
+ const local = Math.floor(ms) + offsetMin * 60_000;
92
+ const z = floorDiv(local, DAY_MS);
93
+ const secondsOfDay = floorDiv(mod(local, DAY_MS), 1000);
94
+ const { y, m, d } = civilFromDays(z);
95
+ return {
96
+ y,
97
+ m,
98
+ d,
99
+ hh: floorDiv(secondsOfDay, 3600),
100
+ mm: floorDiv(mod(secondsOfDay, 3600), 60),
101
+ ss: mod(secondsOfDay, 60),
102
+ dow: mod(z + 4, 7),
103
+ };
104
+ }
105
+ //# sourceMappingURL=civil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"civil.js","sourceRoot":"","sources":["../../src/time-accessors/civil.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,+DAA+D;AAC/D,kDAAkD;AAClD,0EAA0E;AAC1E,8EAA8E;AAE9E,MAAM,MAAM,GAAG,UAAU,CAAC;AAE1B;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAS,EAAE,CAAS;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,CAAS;IACpC,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS;IACnC,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC;IAC5B,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,OAAO,GAAG,GAAG,GAAG,OAAO,CAAC,CAAC,cAAc;IACnD,MAAM,GAAG,GAAG,QAAQ,CAChB,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,EAC1E,GAAG,CACN,CAAC,CAAC,WAAW;IACd,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC1B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW;IAClF,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU;IACjD,MAAM,CAAC,GAAG,GAAG,GAAG,QAAQ,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU;IACzD,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU;IAC/C,OAAO,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IACzD,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,WAAW;IACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW;IAC/E,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,cAAc;IACnF,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,GAAG,OAAO,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CACtB,EAAU,EACV,SAAiB;IAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO;QACH,CAAC;QACD,CAAC;QACD,CAAC;QACD,EAAE,EAAE,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;QAChC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QACzC,EAAE,EAAE,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC;QACzB,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;KACrB,CAAC;AACN,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//\n// Civil-date arithmetic follows Howard Hinnant's public-domain\n// `civil_from_days` / `days_from_civil` algorithm\n// (http://howardhinnant.github.io/date_algorithms.html). Algorithm only —\n// the integer routines are reimplemented in chartlang style, not transcribed.\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Integer floor-division rounding toward −∞ (unlike JS `/ | 0`, which\n * truncates toward `0`). Required so pre-1970 epochs split correctly.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { floorDiv } from \"@invinite-org/chartlang-runtime\";\n * // floorDiv(-1, 86_400_000); // -1, not 0\n */\nexport function floorDiv(a: number, b: number): number {\n return Math.floor(a / b);\n}\n\n/**\n * Non-negative modulo (Euclidean) — `mod(-1, 7) === 6`, unlike JS `%`\n * which would yield `-1`. Pairs with {@link floorDiv} for correct\n * pre-epoch date math.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { mod } from \"@invinite-org/chartlang-runtime\";\n * // mod(-1, 7); // 6\n */\nexport function mod(a: number, b: number): number {\n return a - floorDiv(a, b) * b;\n}\n\n/**\n * The civil date for a count of days since the Unix epoch\n * (1970-01-01). `m` is `1..12`, `d` is `1..31`. Pure integer\n * arithmetic — correct for negative (pre-epoch) `z`.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { civilFromDays } from \"@invinite-org/chartlang-runtime\";\n * // civilFromDays(0); // { y: 1970, m: 1, d: 1 }\n */\nexport function civilFromDays(z: number): { y: number; m: number; d: number } {\n const shifted = z + 719_468;\n const era = floorDiv(shifted >= 0 ? shifted : shifted - 146_096, 146_097);\n const doe = shifted - era * 146_097; // [0, 146096]\n const yoe = floorDiv(\n doe - floorDiv(doe, 1460) + floorDiv(doe, 36_524) - floorDiv(doe, 146_096),\n 365,\n ); // [0, 399]\n const y = yoe + era * 400;\n const doy = doe - (365 * yoe + floorDiv(yoe, 4) - floorDiv(yoe, 100)); // [0, 365]\n const mp = floorDiv(5 * doy + 2, 153); // [0, 11]\n const d = doy - floorDiv(153 * mp + 2, 5) + 1; // [1, 31]\n const m = mp < 10 ? mp + 3 : mp - 9; // [1, 12]\n return { y: m <= 2 ? y + 1 : y, m, d };\n}\n\n/**\n * Days since the Unix epoch (1970-01-01) for a civil `y` / `m` (`1..12`)\n * / `d` (`1..31`). Inverse of {@link civilFromDays}. Pure integer\n * arithmetic — correct for pre-epoch dates.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { daysFromCivil } from \"@invinite-org/chartlang-runtime\";\n * // daysFromCivil(1970, 1, 1); // 0\n */\nexport function daysFromCivil(y: number, m: number, d: number): number {\n const yy = m <= 2 ? y - 1 : y;\n const era = floorDiv(yy >= 0 ? yy : yy - 399, 400);\n const yoe = yy - era * 400; // [0, 399]\n const doy = floorDiv(153 * (m > 2 ? m - 3 : m + 9) + 2, 5) + d - 1; // [0, 365]\n const doe = yoe * 365 + floorDiv(yoe, 4) - floorDiv(yoe, 100) + doy; // [0, 146096]\n return era * 146_097 + doe - 719_468;\n}\n\n/**\n * Split a UTC ms epoch (after pre-shifting by `offsetMin` minutes) into\n * civil fields. `dow` is `0=Sun .. 6=Sat`, derived from the day count\n * (`mod(z + 4, 7)` — 1970-01-01 was a Thursday, index 4). All integer\n * arithmetic on the floored ms — no `Date`.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { splitEpoch } from \"@invinite-org/chartlang-runtime\";\n * // splitEpoch(0, 0); // { y: 1970, m: 1, d: 1, hh: 0, mm: 0, ss: 0, dow: 4 }\n */\nexport function splitEpoch(\n ms: number,\n offsetMin: number,\n): { y: number; m: number; d: number; hh: number; mm: number; ss: number; dow: number } {\n const local = Math.floor(ms) + offsetMin * 60_000;\n const z = floorDiv(local, DAY_MS);\n const secondsOfDay = floorDiv(mod(local, DAY_MS), 1000);\n const { y, m, d } = civilFromDays(z);\n return {\n y,\n m,\n d,\n hh: floorDiv(secondsOfDay, 3600),\n mm: floorDiv(mod(secondsOfDay, 3600), 60),\n ss: mod(secondsOfDay, 60),\n dow: mod(z + 4, 7),\n };\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export { civilFromDays, daysFromCivil, floorDiv, mod, splitEpoch } from "./civil.js";
2
+ export { buildSessionNamespace, createSessionNamespace } from "./sessionAccessors.js";
3
+ export { parseSessionWindowMinutes } from "./sessionWindow.js";
4
+ export { buildTimeNamespace, createTimeNamespace, resolveTz } from "./timeAccessors.js";
5
+ export { buildTzDstReporter } from "./tzDiagnostic.js";
6
+ export { resolveOffsetMinutes } from "./tzOffset.js";
7
+ export type { ResolvedOffset } from "./tzOffset.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/time-accessors/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,9 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ export { civilFromDays, daysFromCivil, floorDiv, mod, splitEpoch } from "./civil.js";
4
+ export { buildSessionNamespace, createSessionNamespace } from "./sessionAccessors.js";
5
+ export { parseSessionWindowMinutes } from "./sessionWindow.js";
6
+ export { buildTimeNamespace, createTimeNamespace, resolveTz } from "./timeAccessors.js";
7
+ export { buildTzDstReporter } from "./tzDiagnostic.js";
8
+ export { resolveOffsetMinutes } from "./tzOffset.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/time-accessors/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAE/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,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 { civilFromDays, daysFromCivil, floorDiv, mod, splitEpoch } from \"./civil.js\";\nexport { buildSessionNamespace, createSessionNamespace } from \"./sessionAccessors.js\";\nexport { parseSessionWindowMinutes } from \"./sessionWindow.js\";\nexport { buildTimeNamespace, createTimeNamespace, resolveTz } from \"./timeAccessors.js\";\nexport { buildTzDstReporter } from \"./tzDiagnostic.js\";\nexport { resolveOffsetMinutes } from \"./tzOffset.js\";\nexport type { ResolvedOffset } from \"./tzOffset.js\";\n"]}
@@ -0,0 +1,50 @@
1
+ import type { SessionNamespace } from "@invinite-org/chartlang-core";
2
+ import type { RuntimeContext } from "../runtimeContext.js";
3
+ /**
4
+ * Build a frozen `session` namespace whose `isOpen` tests half-open membership
5
+ * of an epoch's local minute-of-day against a parsed `"HH:MM-HH:MM"` window —
6
+ * pure integer epoch math (Howard Hinnant `civil_from_days` via
7
+ * {@link splitEpoch}), **no `Date`, no `Intl`** — so the result is
8
+ * byte-reproducible across hosts. The factory closes over the mount's default
9
+ * timezone and a `tz-dst-unsupported` reporter; the `isOpen` body is stateless.
10
+ *
11
+ * Membership is half-open `[start, end)` (the `end` minute is OUT, matching
12
+ * `ta.sessionVolumeProfile` and Pine `time()`); a window with `end <= start`
13
+ * (e.g. `"2200-0400"`) is treated as a midnight wrap `[start, 1440) ∪ [0, end)`.
14
+ * A non-finite `t` or a malformed `spec` yields `false`; the accessor never
15
+ * throws. A DST-bearing IANA zone resolves to UTC and invokes
16
+ * `onDstUnsupported(tz)` (once-per-tz dedup lives in the caller) — fired only
17
+ * once the call is otherwise well-formed (finite `t`, parseable `spec`).
18
+ *
19
+ * Unlike `ta.sessionVolumeProfile`, which defaults its window to
20
+ * `syminfo.session`, `isOpen` takes the `spec` as an explicit argument (often
21
+ * from `input.session`) and never reads `syminfo.session` itself.
22
+ *
23
+ * @since 1.5
24
+ * @stable
25
+ * @example
26
+ * // import { createSessionNamespace } from "@invinite-org/chartlang-runtime";
27
+ * // const session = createSessionNamespace(() => "UTC", () => {});
28
+ * // session.isOpen(0, "0000-1200"); // true (00:00 UTC is in [00:00, 12:00))
29
+ */
30
+ export declare function createSessionNamespace(getDefaultTz: () => string, onDstUnsupported: (tz: string) => void): SessionNamespace;
31
+ /**
32
+ * Install-time builder: bind {@link createSessionNamespace} to a mount's
33
+ * {@link RuntimeContext}. The default timezone resolves from the live
34
+ * `syminfo.timezone` view; the `tz-dst-unsupported` diagnostic dedupes on the
35
+ * SAME `ctx.diagnosedTzKeys` set the `time.*` accessors use, so a script using
36
+ * both `time.*` and `session.isOpen` on one DST zone warns once total.
37
+ * `buildComputeContext` calls this per bar (like the `state` / `request` /
38
+ * `runtime` namespaces, NOT a module constant like `ta`): the returned namespace
39
+ * is a pure view bound to the mount's `RuntimeContext`, so it is cheap to rebuild
40
+ * and is not relied upon to be identity-stable across bars.
41
+ *
42
+ * @since 1.5
43
+ * @stable
44
+ * @example
45
+ * // import { buildSessionNamespace } from "@invinite-org/chartlang-runtime";
46
+ * // const session = buildSessionNamespace(state.runtimeContext);
47
+ * // void session.isOpen;
48
+ */
49
+ export declare function buildSessionNamespace(ctx: RuntimeContext): SessionNamespace;
50
+ //# sourceMappingURL=sessionAccessors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionAccessors.d.ts","sourceRoot":"","sources":["../../src/time-accessors/sessionAccessors.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,8BAA8B,CAAC;AAE3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sBAAsB,CAClC,YAAY,EAAE,MAAM,MAAM,EAC1B,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GACvC,gBAAgB,CAqBlB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,gBAAgB,CAE3E"}
@@ -0,0 +1,79 @@
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 { splitEpoch } from "./civil.js";
4
+ import { parseSessionWindowMinutes } from "./sessionWindow.js";
5
+ import { resolveTz } from "./timeAccessors.js";
6
+ import { buildTzDstReporter } from "./tzDiagnostic.js";
7
+ import { resolveOffsetMinutes } from "./tzOffset.js";
8
+ /**
9
+ * Build a frozen `session` namespace whose `isOpen` tests half-open membership
10
+ * of an epoch's local minute-of-day against a parsed `"HH:MM-HH:MM"` window —
11
+ * pure integer epoch math (Howard Hinnant `civil_from_days` via
12
+ * {@link splitEpoch}), **no `Date`, no `Intl`** — so the result is
13
+ * byte-reproducible across hosts. The factory closes over the mount's default
14
+ * timezone and a `tz-dst-unsupported` reporter; the `isOpen` body is stateless.
15
+ *
16
+ * Membership is half-open `[start, end)` (the `end` minute is OUT, matching
17
+ * `ta.sessionVolumeProfile` and Pine `time()`); a window with `end <= start`
18
+ * (e.g. `"2200-0400"`) is treated as a midnight wrap `[start, 1440) ∪ [0, end)`.
19
+ * A non-finite `t` or a malformed `spec` yields `false`; the accessor never
20
+ * throws. A DST-bearing IANA zone resolves to UTC and invokes
21
+ * `onDstUnsupported(tz)` (once-per-tz dedup lives in the caller) — fired only
22
+ * once the call is otherwise well-formed (finite `t`, parseable `spec`).
23
+ *
24
+ * Unlike `ta.sessionVolumeProfile`, which defaults its window to
25
+ * `syminfo.session`, `isOpen` takes the `spec` as an explicit argument (often
26
+ * from `input.session`) and never reads `syminfo.session` itself.
27
+ *
28
+ * @since 1.5
29
+ * @stable
30
+ * @example
31
+ * // import { createSessionNamespace } from "@invinite-org/chartlang-runtime";
32
+ * // const session = createSessionNamespace(() => "UTC", () => {});
33
+ * // session.isOpen(0, "0000-1200"); // true (00:00 UTC is in [00:00, 12:00))
34
+ */
35
+ export function createSessionNamespace(getDefaultTz, onDstUnsupported) {
36
+ return Object.freeze({
37
+ isOpen(t, spec, tz) {
38
+ if (!Number.isFinite(t))
39
+ return false;
40
+ const parsed = parseSessionWindowMinutes(spec);
41
+ if (parsed === null)
42
+ return false;
43
+ const resolved = resolveTz(tz, getDefaultTz);
44
+ const { offsetMin, dstUnsupported } = resolveOffsetMinutes(resolved);
45
+ if (dstUnsupported)
46
+ onDstUnsupported(resolved);
47
+ const { hh, mm } = splitEpoch(t, offsetMin);
48
+ const minuteOfDay = hh * 60 + mm;
49
+ const { startMinutes, endMinutes } = parsed;
50
+ if (endMinutes <= startMinutes) {
51
+ // Midnight-wrap window: [start, 1440) ∪ [0, end).
52
+ return minuteOfDay >= startMinutes || minuteOfDay < endMinutes;
53
+ }
54
+ return minuteOfDay >= startMinutes && minuteOfDay < endMinutes;
55
+ },
56
+ });
57
+ }
58
+ /**
59
+ * Install-time builder: bind {@link createSessionNamespace} to a mount's
60
+ * {@link RuntimeContext}. The default timezone resolves from the live
61
+ * `syminfo.timezone` view; the `tz-dst-unsupported` diagnostic dedupes on the
62
+ * SAME `ctx.diagnosedTzKeys` set the `time.*` accessors use, so a script using
63
+ * both `time.*` and `session.isOpen` on one DST zone warns once total.
64
+ * `buildComputeContext` calls this per bar (like the `state` / `request` /
65
+ * `runtime` namespaces, NOT a module constant like `ta`): the returned namespace
66
+ * is a pure view bound to the mount's `RuntimeContext`, so it is cheap to rebuild
67
+ * and is not relied upon to be identity-stable across bars.
68
+ *
69
+ * @since 1.5
70
+ * @stable
71
+ * @example
72
+ * // import { buildSessionNamespace } from "@invinite-org/chartlang-runtime";
73
+ * // const session = buildSessionNamespace(state.runtimeContext);
74
+ * // void session.isOpen;
75
+ */
76
+ export function buildSessionNamespace(ctx) {
77
+ return createSessionNamespace(() => ctx.views.syminfo.timezone, buildTzDstReporter(ctx));
78
+ }
79
+ //# sourceMappingURL=sessionAccessors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionAccessors.js","sourceRoot":"","sources":["../../src/time-accessors/sessionAccessors.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAK/D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,sBAAsB,CAClC,YAA0B,EAC1B,gBAAsC;IAEtC,OAAO,MAAM,CAAC,MAAM,CAAmB;QACnC,MAAM,CAAC,CAAO,EAAE,IAAY,EAAE,EAAW;YACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YACtC,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YAElC,MAAM,QAAQ,GAAG,SAAS,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YAC7C,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;YACrE,IAAI,cAAc;gBAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAE/C,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YAC5C,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;YACjC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;YAC5C,IAAI,UAAU,IAAI,YAAY,EAAE,CAAC;gBAC7B,kDAAkD;gBAClD,OAAO,WAAW,IAAI,YAAY,IAAI,WAAW,GAAG,UAAU,CAAC;YACnE,CAAC;YACD,OAAO,WAAW,IAAI,YAAY,IAAI,WAAW,GAAG,UAAU,CAAC;QACnE,CAAC;KACJ,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAmB;IACrD,OAAO,sBAAsB,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7F,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 { SessionNamespace, Time } from \"@invinite-org/chartlang-core\";\n\nimport type { RuntimeContext } from \"../runtimeContext.js\";\nimport { splitEpoch } from \"./civil.js\";\nimport { parseSessionWindowMinutes } from \"./sessionWindow.js\";\nimport { resolveTz } from \"./timeAccessors.js\";\nimport { buildTzDstReporter } from \"./tzDiagnostic.js\";\nimport { resolveOffsetMinutes } from \"./tzOffset.js\";\n\n/**\n * Build a frozen `session` namespace whose `isOpen` tests half-open membership\n * of an epoch's local minute-of-day against a parsed `\"HH:MM-HH:MM\"` window —\n * pure integer epoch math (Howard Hinnant `civil_from_days` via\n * {@link splitEpoch}), **no `Date`, no `Intl`** — so the result is\n * byte-reproducible across hosts. The factory closes over the mount's default\n * timezone and a `tz-dst-unsupported` reporter; the `isOpen` body is stateless.\n *\n * Membership is half-open `[start, end)` (the `end` minute is OUT, matching\n * `ta.sessionVolumeProfile` and Pine `time()`); a window with `end <= start`\n * (e.g. `\"2200-0400\"`) is treated as a midnight wrap `[start, 1440) ∪ [0, end)`.\n * A non-finite `t` or a malformed `spec` yields `false`; the accessor never\n * throws. A DST-bearing IANA zone resolves to UTC and invokes\n * `onDstUnsupported(tz)` (once-per-tz dedup lives in the caller) — fired only\n * once the call is otherwise well-formed (finite `t`, parseable `spec`).\n *\n * Unlike `ta.sessionVolumeProfile`, which defaults its window to\n * `syminfo.session`, `isOpen` takes the `spec` as an explicit argument (often\n * from `input.session`) and never reads `syminfo.session` itself.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { createSessionNamespace } from \"@invinite-org/chartlang-runtime\";\n * // const session = createSessionNamespace(() => \"UTC\", () => {});\n * // session.isOpen(0, \"0000-1200\"); // true (00:00 UTC is in [00:00, 12:00))\n */\nexport function createSessionNamespace(\n getDefaultTz: () => string,\n onDstUnsupported: (tz: string) => void,\n): SessionNamespace {\n return Object.freeze<SessionNamespace>({\n isOpen(t: Time, spec: string, tz?: string): boolean {\n if (!Number.isFinite(t)) return false;\n const parsed = parseSessionWindowMinutes(spec);\n if (parsed === null) return false;\n\n const resolved = resolveTz(tz, getDefaultTz);\n const { offsetMin, dstUnsupported } = resolveOffsetMinutes(resolved);\n if (dstUnsupported) onDstUnsupported(resolved);\n\n const { hh, mm } = splitEpoch(t, offsetMin);\n const minuteOfDay = hh * 60 + mm;\n const { startMinutes, endMinutes } = parsed;\n if (endMinutes <= startMinutes) {\n // Midnight-wrap window: [start, 1440) ∪ [0, end).\n return minuteOfDay >= startMinutes || minuteOfDay < endMinutes;\n }\n return minuteOfDay >= startMinutes && minuteOfDay < endMinutes;\n },\n });\n}\n\n/**\n * Install-time builder: bind {@link createSessionNamespace} to a mount's\n * {@link RuntimeContext}. The default timezone resolves from the live\n * `syminfo.timezone` view; the `tz-dst-unsupported` diagnostic dedupes on the\n * SAME `ctx.diagnosedTzKeys` set the `time.*` accessors use, so a script using\n * both `time.*` and `session.isOpen` on one DST zone warns once total.\n * `buildComputeContext` calls this per bar (like the `state` / `request` /\n * `runtime` namespaces, NOT a module constant like `ta`): the returned namespace\n * is a pure view bound to the mount's `RuntimeContext`, so it is cheap to rebuild\n * and is not relied upon to be identity-stable across bars.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { buildSessionNamespace } from \"@invinite-org/chartlang-runtime\";\n * // const session = buildSessionNamespace(state.runtimeContext);\n * // void session.isOpen;\n */\nexport function buildSessionNamespace(ctx: RuntimeContext): SessionNamespace {\n return createSessionNamespace(() => ctx.views.syminfo.timezone, buildTzDstReporter(ctx));\n}\n"]}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Parse a daily session window `"HH:MM-HH:MM"` / `"HHMM-HHMM"` into start/end
3
+ * minute-of-day (`0..1439`). Returns `null` for any malformed spec or an
4
+ * out-of-range hour (`>23`) / minute (`>59`). A window whose `end <= start`
5
+ * is left as-is — the caller decides whether to treat it as a midnight wrap.
6
+ *
7
+ * @since 1.5
8
+ * @stable
9
+ * @example
10
+ * // import { parseSessionWindowMinutes } from "@invinite-org/chartlang-runtime";
11
+ * // parseSessionWindowMinutes("0930-1600"); // { startMinutes: 570, endMinutes: 960 }
12
+ */
13
+ export declare function parseSessionWindowMinutes(spec: string): {
14
+ startMinutes: number;
15
+ endMinutes: number;
16
+ } | null;
17
+ //# sourceMappingURL=sessionWindow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionWindow.d.ts","sourceRoot":"","sources":["../../src/time-accessors/sessionWindow.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACrC,IAAI,EAAE,MAAM,GACb;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAarD"}
@@ -0,0 +1,41 @@
1
+ // Copyright (c) 2026 Invinite. Licensed under the MIT License.
2
+ // See the LICENSE file in the repo root for full license text.
3
+ //
4
+ // The single source of the chartlang session-window grammar. Lifted out of
5
+ // `ta/sessionVolumeProfile.ts` so `ta.sessionVolumeProfile` and
6
+ // `session.isOpen` parse `"HH:MM-HH:MM"` windows through ONE regex — never a
7
+ // forked copy.
8
+ // Two `HH[:MM]` clock fields separated by `-` (optional surrounding whitespace).
9
+ // The minutes are optional (`"0930"` / `"930"` parse), and a `:` separator is
10
+ // also optional (`"09:30"` and `"0930"` are equivalent).
11
+ const SESSION_WINDOW = /^(\d{1,2})(?::?(\d{2}))?\s*-\s*(\d{1,2})(?::?(\d{2}))?$/;
12
+ /**
13
+ * Parse a daily session window `"HH:MM-HH:MM"` / `"HHMM-HHMM"` into start/end
14
+ * minute-of-day (`0..1439`). Returns `null` for any malformed spec or an
15
+ * out-of-range hour (`>23`) / minute (`>59`). A window whose `end <= start`
16
+ * is left as-is — the caller decides whether to treat it as a midnight wrap.
17
+ *
18
+ * @since 1.5
19
+ * @stable
20
+ * @example
21
+ * // import { parseSessionWindowMinutes } from "@invinite-org/chartlang-runtime";
22
+ * // parseSessionWindowMinutes("0930-1600"); // { startMinutes: 570, endMinutes: 960 }
23
+ */
24
+ export function parseSessionWindowMinutes(spec) {
25
+ const match = SESSION_WINDOW.exec(spec.trim());
26
+ if (match === null)
27
+ return null;
28
+ const startHour = Number(match[1]);
29
+ const startMinute = match[2] === undefined ? 0 : Number(match[2]);
30
+ const endHour = Number(match[3]);
31
+ const endMinute = match[4] === undefined ? 0 : Number(match[4]);
32
+ if (startHour < 0 || startHour > 23 || startMinute < 0 || startMinute > 59)
33
+ return null;
34
+ if (endHour < 0 || endHour > 23 || endMinute < 0 || endMinute > 59)
35
+ return null;
36
+ return {
37
+ startMinutes: startHour * 60 + startMinute,
38
+ endMinutes: endHour * 60 + endMinute,
39
+ };
40
+ }
41
+ //# sourceMappingURL=sessionWindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionWindow.js","sourceRoot":"","sources":["../../src/time-accessors/sessionWindow.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,2EAA2E;AAC3E,gEAAgE;AAChE,6EAA6E;AAC7E,eAAe;AAEf,iFAAiF;AACjF,8EAA8E;AAC9E,yDAAyD;AACzD,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAEjF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,yBAAyB,CACrC,IAAY;IAEZ,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,EAAE,IAAI,WAAW,GAAG,CAAC,IAAI,WAAW,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IACxF,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,EAAE,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAChF,OAAO;QACH,YAAY,EAAE,SAAS,GAAG,EAAE,GAAG,WAAW;QAC1C,UAAU,EAAE,OAAO,GAAG,EAAE,GAAG,SAAS;KACvC,CAAC;AACN,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//\n// The single source of the chartlang session-window grammar. Lifted out of\n// `ta/sessionVolumeProfile.ts` so `ta.sessionVolumeProfile` and\n// `session.isOpen` parse `\"HH:MM-HH:MM\"` windows through ONE regex — never a\n// forked copy.\n\n// Two `HH[:MM]` clock fields separated by `-` (optional surrounding whitespace).\n// The minutes are optional (`\"0930\"` / `\"930\"` parse), and a `:` separator is\n// also optional (`\"09:30\"` and `\"0930\"` are equivalent).\nconst SESSION_WINDOW = /^(\\d{1,2})(?::?(\\d{2}))?\\s*-\\s*(\\d{1,2})(?::?(\\d{2}))?$/;\n\n/**\n * Parse a daily session window `\"HH:MM-HH:MM\"` / `\"HHMM-HHMM\"` into start/end\n * minute-of-day (`0..1439`). Returns `null` for any malformed spec or an\n * out-of-range hour (`>23`) / minute (`>59`). A window whose `end <= start`\n * is left as-is — the caller decides whether to treat it as a midnight wrap.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { parseSessionWindowMinutes } from \"@invinite-org/chartlang-runtime\";\n * // parseSessionWindowMinutes(\"0930-1600\"); // { startMinutes: 570, endMinutes: 960 }\n */\nexport function parseSessionWindowMinutes(\n spec: string,\n): { startMinutes: number; endMinutes: number } | null {\n const match = SESSION_WINDOW.exec(spec.trim());\n if (match === null) return null;\n const startHour = Number(match[1]);\n const startMinute = match[2] === undefined ? 0 : Number(match[2]);\n const endHour = Number(match[3]);\n const endMinute = match[4] === undefined ? 0 : Number(match[4]);\n if (startHour < 0 || startHour > 23 || startMinute < 0 || startMinute > 59) return null;\n if (endHour < 0 || endHour > 23 || endMinute < 0 || endMinute > 59) return null;\n return {\n startMinutes: startHour * 60 + startMinute,\n endMinutes: endHour * 60 + endMinute,\n };\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import type { TimeNamespace } from "@invinite-org/chartlang-core";
2
+ import type { RuntimeContext } from "../runtimeContext.js";
3
+ /**
4
+ * Resolve a tz argument, falling back to the mount default then `"UTC"`.
5
+ * Shared by the `time.*` and `session.*` accessor factories so both honour the
6
+ * identical explicit → `syminfo.timezone` → `"UTC"` precedence.
7
+ *
8
+ * @since 1.5
9
+ * @stable
10
+ * @example
11
+ * // import { resolveTz } from "@invinite-org/chartlang-runtime";
12
+ * // resolveTz(undefined, () => ""); // "UTC"
13
+ */
14
+ export declare function resolveTz(tz: string | undefined, getDefaultTz: () => string): string;
15
+ /**
16
+ * Build a frozen `time` namespace whose accessors do pure integer epoch math
17
+ * (Howard Hinnant `civil_from_days`) — **no `Date`, no `Intl`** — so output is
18
+ * byte-reproducible across hosts. The factory closes over the mount's default
19
+ * timezone, the live bar interval (for {@link TimeNamespace.timeClose}), and a
20
+ * `tz-dst-unsupported` reporter; the accessor bodies themselves are stateless.
21
+ *
22
+ * v1 honours UTC + fixed-offset zones only. A DST-bearing IANA zone resolves to
23
+ * UTC and invokes `onDstUnsupported(tz)` (once-per-tz dedup lives in the
24
+ * caller). Non-finite / out-of-range inputs yield `NaN`; the accessors never
25
+ * throw.
26
+ *
27
+ * @since 1.5
28
+ * @stable
29
+ * @example
30
+ * // import { createTimeNamespace } from "@invinite-org/chartlang-runtime";
31
+ * // const time = createTimeNamespace(() => "UTC", () => 60_000, () => {});
32
+ * // time.year(0); // 1970
33
+ */
34
+ export declare function createTimeNamespace(getDefaultTz: () => string, getIntervalMs: () => number, onDstUnsupported: (tz: string) => void): TimeNamespace;
35
+ /**
36
+ * Install-time builder: bind {@link createTimeNamespace} to a mount's
37
+ * {@link RuntimeContext}. The default timezone resolves from the live
38
+ * `syminfo.timezone` view, the bar interval from `timeframe.inSeconds`, and the
39
+ * `tz-dst-unsupported` diagnostic dedupes once per distinct tz on
40
+ * `ctx.diagnosedTzKeys`. `buildComputeContext` calls this per bar (like the
41
+ * `state` / `request` / `runtime` namespaces, NOT a module constant like `ta`):
42
+ * the returned namespace is a pure view bound to the mount's `RuntimeContext`,
43
+ * so it is cheap to rebuild and is not relied upon to be identity-stable across
44
+ * bars (the script receives a fresh `ctx` each bar).
45
+ *
46
+ * @since 1.5
47
+ * @stable
48
+ * @example
49
+ * // import { buildTimeNamespace } from "@invinite-org/chartlang-runtime";
50
+ * // const time = buildTimeNamespace(state.runtimeContext);
51
+ * // void time.year;
52
+ */
53
+ export declare function buildTimeNamespace(ctx: RuntimeContext): TimeNamespace;
54
+ //# sourceMappingURL=timeAccessors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeAccessors.d.ts","sourceRoot":"","sources":["../../src/time-accessors/timeAccessors.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAQ,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,MAAM,GAAG,MAAM,CAIpF;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAC/B,YAAY,EAAE,MAAM,MAAM,EAC1B,aAAa,EAAE,MAAM,MAAM,EAC3B,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GACvC,aAAa,CAuEf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,aAAa,CAMrE"}
@@ -0,0 +1,132 @@
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 { daysFromCivil, splitEpoch } from "./civil.js";
4
+ import { buildTzDstReporter } from "./tzDiagnostic.js";
5
+ import { resolveOffsetMinutes } from "./tzOffset.js";
6
+ /**
7
+ * Resolve a tz argument, falling back to the mount default then `"UTC"`.
8
+ * Shared by the `time.*` and `session.*` accessor factories so both honour the
9
+ * identical explicit → `syminfo.timezone` → `"UTC"` precedence.
10
+ *
11
+ * @since 1.5
12
+ * @stable
13
+ * @example
14
+ * // import { resolveTz } from "@invinite-org/chartlang-runtime";
15
+ * // resolveTz(undefined, () => ""); // "UTC"
16
+ */
17
+ export function resolveTz(tz, getDefaultTz) {
18
+ if (tz !== undefined && tz !== "")
19
+ return tz;
20
+ const fallback = getDefaultTz();
21
+ return fallback === "" ? "UTC" : fallback;
22
+ }
23
+ function isInt(value) {
24
+ return Number.isInteger(value);
25
+ }
26
+ /**
27
+ * Build a frozen `time` namespace whose accessors do pure integer epoch math
28
+ * (Howard Hinnant `civil_from_days`) — **no `Date`, no `Intl`** — so output is
29
+ * byte-reproducible across hosts. The factory closes over the mount's default
30
+ * timezone, the live bar interval (for {@link TimeNamespace.timeClose}), and a
31
+ * `tz-dst-unsupported` reporter; the accessor bodies themselves are stateless.
32
+ *
33
+ * v1 honours UTC + fixed-offset zones only. A DST-bearing IANA zone resolves to
34
+ * UTC and invokes `onDstUnsupported(tz)` (once-per-tz dedup lives in the
35
+ * caller). Non-finite / out-of-range inputs yield `NaN`; the accessors never
36
+ * throw.
37
+ *
38
+ * @since 1.5
39
+ * @stable
40
+ * @example
41
+ * // import { createTimeNamespace } from "@invinite-org/chartlang-runtime";
42
+ * // const time = createTimeNamespace(() => "UTC", () => 60_000, () => {});
43
+ * // time.year(0); // 1970
44
+ */
45
+ export function createTimeNamespace(getDefaultTz, getIntervalMs, onDstUnsupported) {
46
+ function offsetFor(tz) {
47
+ const resolved = resolveTz(tz, getDefaultTz);
48
+ const { offsetMin, dstUnsupported } = resolveOffsetMinutes(resolved);
49
+ if (dstUnsupported)
50
+ onDstUnsupported(resolved);
51
+ return offsetMin;
52
+ }
53
+ function field(t, tz, key) {
54
+ const offsetMin = offsetFor(tz);
55
+ if (!Number.isFinite(t))
56
+ return Number.NaN;
57
+ return splitEpoch(t, offsetMin)[key];
58
+ }
59
+ return Object.freeze({
60
+ year: (t, tz) => field(t, tz, "y"),
61
+ month: (t, tz) => field(t, tz, "m"),
62
+ dayofmonth: (t, tz) => field(t, tz, "d"),
63
+ hour: (t, tz) => field(t, tz, "hh"),
64
+ minute: (t, tz) => field(t, tz, "mm"),
65
+ second: (t, tz) => field(t, tz, "ss"),
66
+ dayofweek: (t, tz) => {
67
+ const offsetMin = offsetFor(tz);
68
+ if (!Number.isFinite(t))
69
+ return Number.NaN;
70
+ // Pine convention: 1=Sunday .. 7=Saturday (splitEpoch dow is 0=Sun).
71
+ return splitEpoch(t, offsetMin).dow + 1;
72
+ },
73
+ timestamp: (year, month, day, hour, minute, second, tz) => {
74
+ const offsetMin = offsetFor(tz);
75
+ const hh = hour ?? 0;
76
+ const mm = minute ?? 0;
77
+ const ss = second ?? 0;
78
+ if (!isInt(year) ||
79
+ !isInt(month) ||
80
+ !isInt(day) ||
81
+ !isInt(hh) ||
82
+ !isInt(mm) ||
83
+ !isInt(ss) ||
84
+ month < 1 ||
85
+ month > 12 ||
86
+ day < 1 ||
87
+ day > 31 ||
88
+ hh < 0 ||
89
+ hh > 23 ||
90
+ mm < 0 ||
91
+ mm > 59 ||
92
+ ss < 0 ||
93
+ ss > 59) {
94
+ return Number.NaN;
95
+ }
96
+ return (daysFromCivil(year, month, day) * 86_400_000 +
97
+ (hh * 3600 + mm * 60 + ss) * 1000 -
98
+ offsetMin * 60_000);
99
+ },
100
+ timeClose: (t, tz) => {
101
+ // `tz` is accepted for surface symmetry; the close instant is
102
+ // tz-invariant (start + interval). A DST tz still flags for
103
+ // consistency with the other accessors.
104
+ offsetFor(tz);
105
+ if (!Number.isFinite(t))
106
+ return Number.NaN;
107
+ return t + getIntervalMs();
108
+ },
109
+ });
110
+ }
111
+ /**
112
+ * Install-time builder: bind {@link createTimeNamespace} to a mount's
113
+ * {@link RuntimeContext}. The default timezone resolves from the live
114
+ * `syminfo.timezone` view, the bar interval from `timeframe.inSeconds`, and the
115
+ * `tz-dst-unsupported` diagnostic dedupes once per distinct tz on
116
+ * `ctx.diagnosedTzKeys`. `buildComputeContext` calls this per bar (like the
117
+ * `state` / `request` / `runtime` namespaces, NOT a module constant like `ta`):
118
+ * the returned namespace is a pure view bound to the mount's `RuntimeContext`,
119
+ * so it is cheap to rebuild and is not relied upon to be identity-stable across
120
+ * bars (the script receives a fresh `ctx` each bar).
121
+ *
122
+ * @since 1.5
123
+ * @stable
124
+ * @example
125
+ * // import { buildTimeNamespace } from "@invinite-org/chartlang-runtime";
126
+ * // const time = buildTimeNamespace(state.runtimeContext);
127
+ * // void time.year;
128
+ */
129
+ export function buildTimeNamespace(ctx) {
130
+ return createTimeNamespace(() => ctx.views.syminfo.timezone, () => ctx.views.timeframe.inSeconds * 1000, buildTzDstReporter(ctx));
131
+ }
132
+ //# sourceMappingURL=timeAccessors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeAccessors.js","sourceRoot":"","sources":["../../src/time-accessors/timeAccessors.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 { Time, TimeNamespace } from \"@invinite-org/chartlang-core\";\n\nimport type { RuntimeContext } from \"../runtimeContext.js\";\nimport { daysFromCivil, splitEpoch } from \"./civil.js\";\nimport { buildTzDstReporter } from \"./tzDiagnostic.js\";\nimport { resolveOffsetMinutes } from \"./tzOffset.js\";\n\n/**\n * Resolve a tz argument, falling back to the mount default then `\"UTC\"`.\n * Shared by the `time.*` and `session.*` accessor factories so both honour the\n * identical explicit → `syminfo.timezone` → `\"UTC\"` precedence.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { resolveTz } from \"@invinite-org/chartlang-runtime\";\n * // resolveTz(undefined, () => \"\"); // \"UTC\"\n */\nexport function resolveTz(tz: string | undefined, getDefaultTz: () => string): string {\n if (tz !== undefined && tz !== \"\") return tz;\n const fallback = getDefaultTz();\n return fallback === \"\" ? \"UTC\" : fallback;\n}\n\nfunction isInt(value: number): boolean {\n return Number.isInteger(value);\n}\n\n/**\n * Build a frozen `time` namespace whose accessors do pure integer epoch math\n * (Howard Hinnant `civil_from_days`) — **no `Date`, no `Intl`** — so output is\n * byte-reproducible across hosts. The factory closes over the mount's default\n * timezone, the live bar interval (for {@link TimeNamespace.timeClose}), and a\n * `tz-dst-unsupported` reporter; the accessor bodies themselves are stateless.\n *\n * v1 honours UTC + fixed-offset zones only. A DST-bearing IANA zone resolves to\n * UTC and invokes `onDstUnsupported(tz)` (once-per-tz dedup lives in the\n * caller). Non-finite / out-of-range inputs yield `NaN`; the accessors never\n * throw.\n *\n * @since 1.5\n * @stable\n * @example\n * // import { createTimeNamespace } from \"@invinite-org/chartlang-runtime\";\n * // const time = createTimeNamespace(() => \"UTC\", () => 60_000, () => {});\n * // time.year(0); // 1970\n */\nexport function createTimeNamespace(\n getDefaultTz: () => string,\n getIntervalMs: () => number,\n onDstUnsupported: (tz: string) => void,\n): TimeNamespace {\n function offsetFor(tz: string | undefined): number {\n const resolved = resolveTz(tz, getDefaultTz);\n const { offsetMin, dstUnsupported } = resolveOffsetMinutes(resolved);\n if (dstUnsupported) onDstUnsupported(resolved);\n return offsetMin;\n }\n\n function field<K extends \"y\" | \"m\" | \"d\" | \"hh\" | \"mm\" | \"ss\">(\n t: Time,\n tz: string | undefined,\n key: K,\n ): number {\n const offsetMin = offsetFor(tz);\n if (!Number.isFinite(t)) return Number.NaN;\n return splitEpoch(t, offsetMin)[key];\n }\n\n return Object.freeze<TimeNamespace>({\n year: (t, tz) => field(t, tz, \"y\"),\n month: (t, tz) => field(t, tz, \"m\"),\n dayofmonth: (t, tz) => field(t, tz, \"d\"),\n hour: (t, tz) => field(t, tz, \"hh\"),\n minute: (t, tz) => field(t, tz, \"mm\"),\n second: (t, tz) => field(t, tz, \"ss\"),\n dayofweek: (t, tz) => {\n const offsetMin = offsetFor(tz);\n if (!Number.isFinite(t)) return Number.NaN;\n // Pine convention: 1=Sunday .. 7=Saturday (splitEpoch dow is 0=Sun).\n return splitEpoch(t, offsetMin).dow + 1;\n },\n timestamp: (year, month, day, hour, minute, second, tz) => {\n const offsetMin = offsetFor(tz);\n const hh = hour ?? 0;\n const mm = minute ?? 0;\n const ss = second ?? 0;\n if (\n !isInt(year) ||\n !isInt(month) ||\n !isInt(day) ||\n !isInt(hh) ||\n !isInt(mm) ||\n !isInt(ss) ||\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > 31 ||\n hh < 0 ||\n hh > 23 ||\n mm < 0 ||\n mm > 59 ||\n ss < 0 ||\n ss > 59\n ) {\n return Number.NaN;\n }\n return (\n daysFromCivil(year, month, day) * 86_400_000 +\n (hh * 3600 + mm * 60 + ss) * 1000 -\n offsetMin * 60_000\n );\n },\n timeClose: (t, tz) => {\n // `tz` is accepted for surface symmetry; the close instant is\n // tz-invariant (start + interval). A DST tz still flags for\n // consistency with the other accessors.\n offsetFor(tz);\n if (!Number.isFinite(t)) return Number.NaN;\n return t + getIntervalMs();\n },\n });\n}\n\n/**\n * Install-time builder: bind {@link createTimeNamespace} to a mount's\n * {@link RuntimeContext}. The default timezone resolves from the live\n * `syminfo.timezone` view, the bar interval from `timeframe.inSeconds`, and the\n * `tz-dst-unsupported` diagnostic dedupes once per distinct tz on\n * `ctx.diagnosedTzKeys`. `buildComputeContext` calls this per bar (like the\n * `state` / `request` / `runtime` namespaces, NOT a module constant like `ta`):\n * the returned namespace is a pure view bound to the mount's `RuntimeContext`,\n * so it is cheap to rebuild and is not relied upon to be identity-stable across\n * bars (the script receives a fresh `ctx` each bar).\n *\n * @since 1.5\n * @stable\n * @example\n * // import { buildTimeNamespace } from \"@invinite-org/chartlang-runtime\";\n * // const time = buildTimeNamespace(state.runtimeContext);\n * // void time.year;\n */\nexport function buildTimeNamespace(ctx: RuntimeContext): TimeNamespace {\n return createTimeNamespace(\n () => ctx.views.syminfo.timezone,\n () => ctx.views.timeframe.inSeconds * 1000,\n buildTzDstReporter(ctx),\n );\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import type { RuntimeContext } from "../runtimeContext.js";
2
+ /**
3
+ * Build the `onDstUnsupported(tz)` reporter the `time.*` and `session.*`
4
+ * accessor factories share. A DST-bearing IANA zone the UTC-first runtime
5
+ * cannot honour warns at most once per distinct tz per mount, deduped on
6
+ * `ctx.diagnosedTzKeys` — so a script using BOTH `time.*` and `session.isOpen`
7
+ * on one DST zone fires `tz-dst-unsupported` once total.
8
+ *
9
+ * @since 1.5
10
+ * @stable
11
+ * @example
12
+ * // import { buildTzDstReporter } from "@invinite-org/chartlang-runtime";
13
+ * // const report = buildTzDstReporter(ctx);
14
+ * // report("America/New_York");
15
+ */
16
+ export declare function buildTzDstReporter(ctx: RuntimeContext): (tz: string) => void;
17
+ //# sourceMappingURL=tzDiagnostic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tzDiagnostic.d.ts","sourceRoot":"","sources":["../../src/time-accessors/tzDiagnostic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAc5E"}