@invinite-org/chartlang-runtime 1.2.0 → 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.
- package/CHANGELOG.md +71 -0
- package/dist/buildComputeContext.d.ts.map +1 -1
- package/dist/buildComputeContext.js +6 -1
- package/dist/buildComputeContext.js.map +1 -1
- package/dist/createScriptRunner.d.ts.map +1 -1
- package/dist/createScriptRunner.js +37 -7
- package/dist/createScriptRunner.js.map +1 -1
- package/dist/dep/DepRunner.d.ts +7 -0
- package/dist/dep/DepRunner.d.ts.map +1 -1
- package/dist/dep/DepRunner.js +3 -0
- package/dist/dep/DepRunner.js.map +1 -1
- package/dist/emit/barcolor.d.ts +44 -0
- package/dist/emit/barcolor.d.ts.map +1 -0
- package/dist/emit/barcolor.js +40 -0
- package/dist/emit/barcolor.js.map +1 -0
- package/dist/emit/bgcolor.d.ts +44 -0
- package/dist/emit/bgcolor.d.ts.map +1 -0
- package/dist/emit/bgcolor.js +45 -0
- package/dist/emit/bgcolor.js.map +1 -0
- package/dist/emit/index.d.ts +2 -0
- package/dist/emit/index.d.ts.map +1 -1
- package/dist/emit/index.js +2 -0
- package/dist/emit/index.js.map +1 -1
- package/dist/emit/plot.d.ts +23 -1
- package/dist/emit/plot.d.ts.map +1 -1
- package/dist/emit/plot.js +32 -1
- package/dist/emit/plot.js.map +1 -1
- package/dist/execution/dispose.d.ts.map +1 -1
- package/dist/execution/dispose.js +3 -1
- package/dist/execution/dispose.js.map +1 -1
- package/dist/execution/runComputeStep.d.ts.map +1 -1
- package/dist/execution/runComputeStep.js +3 -1
- package/dist/execution/runComputeStep.js.map +1 -1
- package/dist/inputs/resolveInputs.js +1 -0
- package/dist/inputs/resolveInputs.js.map +1 -1
- package/dist/persistentStateStore.runtime.d.ts.map +1 -1
- package/dist/persistentStateStore.runtime.js +10 -5
- package/dist/persistentStateStore.runtime.js.map +1 -1
- package/dist/primitives.d.ts +1 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +7 -1
- package/dist/primitives.js.map +1 -1
- package/dist/request/lowerTf.d.ts.map +1 -1
- package/dist/request/lowerTf.js +6 -0
- package/dist/request/lowerTf.js.map +1 -1
- package/dist/request/requestNamespace.d.ts.map +1 -1
- package/dist/request/requestNamespace.js +17 -3
- package/dist/request/requestNamespace.js.map +1 -1
- package/dist/request/security.d.ts +23 -6
- package/dist/request/security.d.ts.map +1 -1
- package/dist/request/security.js +64 -29
- package/dist/request/security.js.map +1 -1
- package/dist/request/securityExprRunner.d.ts +12 -8
- package/dist/request/securityExprRunner.d.ts.map +1 -1
- package/dist/request/securityExprRunner.js +32 -14
- package/dist/request/securityExprRunner.js.map +1 -1
- package/dist/ringBuffer.d.ts +19 -0
- package/dist/ringBuffer.d.ts.map +1 -1
- package/dist/ringBuffer.js +23 -0
- package/dist/ringBuffer.js.map +1 -1
- package/dist/runtimeContext.d.ts +49 -12
- package/dist/runtimeContext.d.ts.map +1 -1
- package/dist/runtimeContext.js.map +1 -1
- package/dist/state/arrayPersistence.d.ts +48 -0
- package/dist/state/arrayPersistence.d.ts.map +1 -0
- package/dist/state/arrayPersistence.js +88 -0
- package/dist/state/arrayPersistence.js.map +1 -0
- package/dist/state/arrayStateSlot.d.ts +78 -0
- package/dist/state/arrayStateSlot.d.ts.map +1 -0
- package/dist/state/arrayStateSlot.js +116 -0
- package/dist/state/arrayStateSlot.js.map +1 -0
- package/dist/state/index.d.ts +3 -1
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +3 -1
- package/dist/state/index.js.map +1 -1
- package/dist/state/lifecycle.d.ts +28 -0
- package/dist/state/lifecycle.d.ts.map +1 -1
- package/dist/state/lifecycle.js +36 -0
- package/dist/state/lifecycle.js.map +1 -1
- package/dist/state/stateNamespace.d.ts.map +1 -1
- package/dist/state/stateNamespace.js +27 -0
- package/dist/state/stateNamespace.js.map +1 -1
- package/dist/ta/sessionVolumeProfile.d.ts.map +1 -1
- package/dist/ta/sessionVolumeProfile.js +1 -17
- package/dist/ta/sessionVolumeProfile.js.map +1 -1
- package/dist/time-accessors/civil.d.ts +73 -0
- package/dist/time-accessors/civil.d.ts.map +1 -0
- package/dist/time-accessors/civil.js +105 -0
- package/dist/time-accessors/civil.js.map +1 -0
- package/dist/time-accessors/index.d.ts +8 -0
- package/dist/time-accessors/index.d.ts.map +1 -0
- package/dist/time-accessors/index.js +9 -0
- package/dist/time-accessors/index.js.map +1 -0
- package/dist/time-accessors/sessionAccessors.d.ts +50 -0
- package/dist/time-accessors/sessionAccessors.d.ts.map +1 -0
- package/dist/time-accessors/sessionAccessors.js +79 -0
- package/dist/time-accessors/sessionAccessors.js.map +1 -0
- package/dist/time-accessors/sessionWindow.d.ts +17 -0
- package/dist/time-accessors/sessionWindow.d.ts.map +1 -0
- package/dist/time-accessors/sessionWindow.js +41 -0
- package/dist/time-accessors/sessionWindow.js.map +1 -0
- package/dist/time-accessors/timeAccessors.d.ts +54 -0
- package/dist/time-accessors/timeAccessors.d.ts.map +1 -0
- package/dist/time-accessors/timeAccessors.js +132 -0
- package/dist/time-accessors/timeAccessors.js.map +1 -0
- package/dist/time-accessors/tzDiagnostic.d.ts +17 -0
- package/dist/time-accessors/tzDiagnostic.d.ts.map +1 -0
- package/dist/time-accessors/tzDiagnostic.js +34 -0
- package/dist/time-accessors/tzDiagnostic.js.map +1 -0
- package/dist/time-accessors/tzOffset.d.ts +31 -0
- package/dist/time-accessors/tzOffset.d.ts.map +1 -0
- package/dist/time-accessors/tzOffset.js +67 -0
- package/dist/time-accessors/tzOffset.js.map +1 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/state/lifecycle.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAG/D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAqB3F;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAmB;IACxD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAChD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,GAAmB;IAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,UAAU,CAAC,GAAG,CAA6B,GAAG,EAAE;YAChD,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC5B,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAmB;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,GAAG;YACP,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;YACzC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;SAC5C,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC7B,GAAmB,EACnB,KAAwC;IAExC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAmB;IAClD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAmB;IACjD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAChD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport type { RuntimeContext } from \"../runtimeContext.js\";\nimport { advanceSeriesSlot, commitSeriesSlot, resetSeriesSlotHead } from \"./seriesSlot.js\";\n\n/**\n * Persisted representation for a runtime state slot. Keys use\n * `${slotId}:state`, matching the compiler-injected slot id plus the\n * state namespace suffix.\n *\n * @since 0.4\n * @stable\n * @example\n * const snapshot: StateSlotSnapshot<number> = {\n * committed: 1,\n * tentative: 2,\n * };\n * void snapshot;\n */\nexport type StateSlotSnapshot<T> = {\n readonly committed: T;\n readonly tentative: T;\n};\n\n/**\n * Reset all non-`state.tick.*` tentative values before tick compute.\n *\n * @since 0.4\n * @stable\n * @example\n * // resetTentativeStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function resetTentativeStateSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.stateSlots.values()) {\n slot.onBarTick();\n }\n}\n\n/**\n * Commit all non-`state.tick.*` tentative values after close compute.\n *\n * @since 0.4\n * @stable\n * @example\n * // commitStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function commitStateSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.stateSlots.values()) {\n slot.onBarClose();\n }\n}\n\n/**\n * Flush runtime state slots into the backing {@link StateStore}.\n *\n * @since 0.4\n * @stable\n * @example\n * // flushStateSlots(ctx);\n * const called = true;\n * void called;\n */\nexport function flushStateSlots(ctx: RuntimeContext): void {\n for (const [key, slot] of ctx.stateSlots.entries()) {\n ctx.stateStore.set<StateSlotSnapshot<unknown>>(key, {\n committed: slot.committed,\n tentative: slot.tentative,\n });\n }\n}\n\n/**\n * Serialise runtime state slots into a snapshot payload.\n *\n * @since 0.5\n * @stable\n * @example\n * // const slots = serialiseStateSlots(ctx);\n * const slots = {};\n * void slots;\n */\nexport function serialiseStateSlots(ctx: RuntimeContext): Readonly<Record<string, unknown>> {\n const out: Record<string, unknown> = {};\n for (const [key, slot] of ctx.stateSlots.entries()) {\n out[key] = {\n committed: slot.serialise(slot.committed),\n tentative: slot.serialise(slot.tentative),\n };\n }\n return Object.freeze(out);\n}\n\n/**\n * Seed restored state-slot payloads into the backing slot store.\n *\n * @since 0.5\n * @stable\n * @example\n * // restoreStateSlots(ctx, snapshot.slots);\n * const restored = true;\n * void restored;\n */\nexport function restoreStateSlots(\n ctx: RuntimeContext,\n slots: Readonly<Record<string, unknown>>,\n): void {\n ctx.stateSlots.clear();\n for (const [key, value] of Object.entries(slots)) {\n ctx.stateStore.set(key, value);\n }\n}\n\n/**\n * Advance every `state.series` ring once for a new close bar — append a\n * fresh `NaN` head so the prior committed head slides to index 1. Runs\n * BEFORE compute on close, so a slot first allocated mid-compute (already\n * holding its seeded head) is not present here and is not double-advanced.\n *\n * @since 0.9\n * @stable\n * @example\n * // advanceSeriesSlots(ctx);\n * const advanced = true;\n * void advanced;\n */\nexport function advanceSeriesSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.seriesSlots.values()) {\n advanceSeriesSlot(slot);\n }\n}\n\n/**\n * Commit every `state.series` live head as its bar-close value after\n * close compute, so the next advance retains it and a tick can reset to\n * it.\n *\n * @since 0.9\n * @stable\n * @example\n * // commitSeriesSlots(ctx);\n * const committed = true;\n * void committed;\n */\nexport function commitSeriesSlots(ctx: RuntimeContext): void {\n for (const slot of ctx.seriesSlots.values()) {\n commitSeriesSlot(slot);\n }\n}\n\n/**\n * Reset every `state.series` live head to its last committed value before\n * tick compute, so a re-write refines from the committed baseline and a\n * tick without a write reads the committed head. Does NOT advance length.\n *\n * @since 0.9\n * @stable\n * @example\n * // resetSeriesHeads(ctx);\n * const reset = true;\n * void reset;\n */\nexport function resetSeriesHeads(ctx: RuntimeContext): void {\n for (const slot of ctx.seriesSlots.values()) {\n resetSeriesSlotHead(slot);\n }\n}\n"]}
|
|
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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stateNamespace.d.ts","sourceRoot":"","sources":["../../src/state/stateNamespace.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// See the LICENSE file in the repo root for full license text.
|
|
3
3
|
import { Float64RingBuffer } from "../ringBuffer.js";
|
|
4
4
|
import { ACTIVE_RUNTIME_CONTEXT } from "../runtimeContext.js";
|
|
5
|
+
import { createArrayStateSlot } from "./arrayStateSlot.js";
|
|
5
6
|
import { createSeriesSlot } from "./seriesSlot.js";
|
|
6
7
|
import { asMutableSlot, StateSlot } from "./stateSlot.js";
|
|
7
8
|
/**
|
|
@@ -26,6 +27,16 @@ const stateKey = (ctx, slotId) => `${ctx.slotIdPrefix ?? ""}${slotId}:state`;
|
|
|
26
27
|
* @internal
|
|
27
28
|
*/
|
|
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`;
|
|
29
40
|
function getCtx(name) {
|
|
30
41
|
const ctx = ACTIVE_RUNTIME_CONTEXT.current;
|
|
31
42
|
if (ctx === null) {
|
|
@@ -63,6 +74,21 @@ function getOrAllocateSeries(slotId, init) {
|
|
|
63
74
|
ctx.seriesSlots.set(key, slot);
|
|
64
75
|
return slot.view;
|
|
65
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
|
+
}
|
|
66
92
|
/**
|
|
67
93
|
* Build the runtime `state` namespace installed on `ComputeContext`.
|
|
68
94
|
* Each function accepts the compiler-injected `slotId` as its first
|
|
@@ -81,6 +107,7 @@ export function buildStateNamespace() {
|
|
|
81
107
|
bool: (slotId, init) => getOrAllocate("state.bool", slotId, init, false),
|
|
82
108
|
string: (slotId, init) => getOrAllocate("state.string", slotId, init, false),
|
|
83
109
|
series: (slotId, init) => getOrAllocateSeries(slotId, init),
|
|
110
|
+
array: (slotId, capacity) => getOrAllocateArray(slotId, capacity),
|
|
84
111
|
tick: {
|
|
85
112
|
float: (slotId, init) => getOrAllocate("state.tick.float", slotId, init, true),
|
|
86
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, NumberSeriesSlot, StateNamespace } from \"@invinite-org/chartlang-core\";\n\nimport { Float64RingBuffer } from \"../ringBuffer.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.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\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\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 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionVolumeProfile.d.ts","sourceRoot":"","sources":["../../src/ta/sessionVolumeProfile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACR,wBAAwB,EACxB,0BAA0B,EAC7B,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"sessionVolumeProfile.d.ts","sourceRoot":"","sources":["../../src/ta/sessionVolumeProfile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACR,wBAAwB,EACxB,0BAA0B,EAC7B,MAAM,8BAA8B,CAAC;AAwItC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,wBAAwB,GAChC,0BAA0B,CAwB5B"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { pushDiagnostic } from "../emit/index.js";
|
|
7
7
|
import { ACTIVE_RUNTIME_CONTEXT } from "../runtimeContext.js";
|
|
8
8
|
import { makeSeriesView, makeShiftedSeriesView } from "../seriesView.js";
|
|
9
|
+
import { parseSessionWindowMinutes } from "../time-accessors/sessionWindow.js";
|
|
9
10
|
import { commitVolumeProfileSnapshot, createVolumeProfileCore, emitVolumeProfileHistogram, resolveVolumeProfileSnapshot, volumeProfileConfigFromOpts, } from "./lib/volume-profile/index.js";
|
|
10
11
|
const DAY_MS = 86_400_000;
|
|
11
12
|
function getCtx() {
|
|
@@ -51,23 +52,6 @@ function resultForOffset(slot, offset) {
|
|
|
51
52
|
function utcDayStart(time) {
|
|
52
53
|
return Math.floor(time / DAY_MS) * DAY_MS;
|
|
53
54
|
}
|
|
54
|
-
function parseSessionWindowMinutes(session) {
|
|
55
|
-
const match = /^(\d{1,2})(?::?(\d{2}))?\s*-\s*(\d{1,2})(?::?(\d{2}))?$/.exec(session.trim());
|
|
56
|
-
if (match === null)
|
|
57
|
-
return null;
|
|
58
|
-
const startHour = Number(match[1]);
|
|
59
|
-
const startMinute = match[2] === undefined ? 0 : Number(match[2]);
|
|
60
|
-
const endHour = Number(match[3]);
|
|
61
|
-
const endMinute = match[4] === undefined ? 0 : Number(match[4]);
|
|
62
|
-
if (startHour < 0 || startHour > 23 || startMinute < 0 || startMinute > 59)
|
|
63
|
-
return null;
|
|
64
|
-
if (endHour < 0 || endHour > 23 || endMinute < 0 || endMinute > 59)
|
|
65
|
-
return null;
|
|
66
|
-
return {
|
|
67
|
-
startMinutes: startHour * 60 + startMinute,
|
|
68
|
-
endMinutes: endHour * 60 + endMinute,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
55
|
function sessionBoundaryFromDescriptor(time, session) {
|
|
72
56
|
const parsed = parseSessionWindowMinutes(session);
|
|
73
57
|
if (parsed === null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionVolumeProfile.js","sourceRoot":"","sources":["../../src/ta/sessionVolumeProfile.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// Ported from ../invinite/src/components/trading-chart/indicators/session-volume-profile.ts\n// @ 3234c8c0c3f9880d9d1e3a3ee63ebd55ddd535f4.\n// Translated, not transcribed — Series<T> shape, opts.offset, JSDoc.\n// See packages/runtime/src/ta/CLAUDE.md for the port convention.\n\nimport type {\n SessionVolumeProfileOpts,\n SessionVolumeProfileResult,\n} from \"@invinite-org/chartlang-core\";\n\nimport { pushDiagnostic } from \"../emit/index.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\nimport {\n type VolumeProfileBar,\n type VolumeProfileCore,\n commitVolumeProfileSnapshot,\n createVolumeProfileCore,\n emitVolumeProfileHistogram,\n resolveVolumeProfileSnapshot,\n volumeProfileConfigFromOpts,\n} from \"./lib/volume-profile/index.js\";\n\nconst DAY_MS = 86_400_000;\n\ntype SessionVolumeProfileSlot = VolumeProfileCore & {\n readonly result: SessionVolumeProfileResult;\n readonly shiftedResults: Map<number, SessionVolumeProfileResult>;\n};\n\ntype MutableSessionVolumeProfileSlot = Omit<SessionVolumeProfileSlot, \"result\"> & {\n result: SessionVolumeProfileResult;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.sessionVolumeProfile called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number): SessionVolumeProfileSlot {\n const core = createVolumeProfileCore(capacity);\n const slot: MutableSessionVolumeProfileSlot = {\n ...core,\n result: Object.freeze({\n get buckets() {\n return slot.buckets;\n },\n poc: makeSeriesView<number>(core.pocBuffer),\n valHigh: makeSeriesView<number>(core.valHighBuffer),\n valLow: makeSeriesView<number>(core.valLowBuffer),\n }),\n shiftedResults: new Map(),\n };\n return slot;\n}\n\nfunction resultForOffset(\n slot: SessionVolumeProfileSlot,\n offset: number,\n): SessionVolumeProfileResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n get buckets() {\n return slot.buckets;\n },\n poc: makeShiftedSeriesView<number>(slot.pocBuffer, offset),\n valHigh: makeShiftedSeriesView<number>(slot.valHighBuffer, offset),\n valLow: makeShiftedSeriesView<number>(slot.valLowBuffer, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\nfunction utcDayStart(time: number): number {\n return Math.floor(time / DAY_MS) * DAY_MS;\n}\n\nfunction parseSessionWindowMinutes(\n session: string,\n): { startMinutes: number; endMinutes: number } | null {\n const match = /^(\\d{1,2})(?::?(\\d{2}))?\\s*-\\s*(\\d{1,2})(?::?(\\d{2}))?$/.exec(session.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\nfunction sessionBoundaryFromDescriptor(time: number, session: string): number | null {\n const parsed = parseSessionWindowMinutes(session);\n if (parsed === null) return null;\n const dayStart = utcDayStart(time);\n const boundary = dayStart + parsed.startMinutes * 60_000;\n return time >= boundary ? boundary : boundary - DAY_MS;\n}\n\nfunction diagnoseMissingSession(ctx: RuntimeContext, slotId: string): void {\n const key = `session-info-missing|${slotId}`;\n if (ctx.diagnosedRequestKeys.has(key)) return;\n ctx.diagnosedRequestKeys.add(key);\n pushDiagnostic(ctx.emissions, {\n kind: \"diagnostic\",\n severity: \"warning\",\n code: \"session-info-missing\",\n message:\n \"Adapter did not provide syminfo.session; ta.sessionVolumeProfile used UTC-day boundaries.\",\n slotId,\n bar: ctx.barIndex(),\n });\n}\n\nfunction resolveSessionStart(\n ctx: RuntimeContext,\n slotId: string,\n opts: SessionVolumeProfileOpts | undefined,\n): number {\n if (opts?.sessionStart !== undefined) return opts.sessionStart;\n const session = ctx.views.syminfo.session;\n if (!ctx.capabilities.symInfoFields.has(\"session\") || session === \"\") {\n diagnoseMissingSession(ctx, slotId);\n return utcDayStart(ctx.stream.bar.time);\n }\n const boundary = sessionBoundaryFromDescriptor(ctx.stream.bar.time, session);\n if (boundary === null) {\n diagnoseMissingSession(ctx, slotId);\n return utcDayStart(ctx.stream.bar.time);\n }\n return boundary;\n}\n\nfunction collectBars(ctx: RuntimeContext, sessionStart: number): ReadonlyArray<VolumeProfileBar> {\n const { ohlcv } = ctx.stream;\n const bars: VolumeProfileBar[] = [];\n for (let lookback = ohlcv.close.length - 1; lookback >= 0; lookback -= 1) {\n const time = ohlcv.time.at(lookback);\n if (time <= sessionStart) continue;\n bars.push({\n close: ohlcv.close.at(lookback),\n high: ohlcv.high.at(lookback),\n low: ohlcv.low.at(lookback),\n open: ohlcv.open.at(lookback),\n time,\n volume: ohlcv.volume.at(lookback),\n });\n }\n return bars;\n}\n\n/**\n * Session Volume Profile — bucketizes the current session's volume\n * by price, resetting when `bar.time` crosses the active session\n * boundary.\n *\n * @formula Port of invinite session-vp: find the current session\n * window, bucket volume by price, then derive POC / value-\n * area high / value-area low via the shared volume-profile\n * pipeline.\n * @anchors `syminfo.session` descriptor, or `opts.sessionStart`\n * override when provided.\n * @warmup NaN until a session window has positive volume; missing\n * `syminfo.session` falls back to UTC-day boundaries.\n * @since 0.5\n * @stable\n * @example\n * // import { plot, ta } from \"@invinite-org/chartlang-core\";\n * // const vp = ta.sessionVolumeProfile({ rowSize: 24 });\n * // plot(vp.poc, { style: { kind: \"horizontal-histogram\", buckets: vp.buckets } });\n */\nexport function sessionVolumeProfile(\n slotId: string,\n opts?: SessionVolumeProfileOpts,\n): SessionVolumeProfileResult {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as SessionVolumeProfileSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n\n const sessionStart = resolveSessionStart(ctx, slotId, opts);\n const bars = ctx.stream.bar.time <= sessionStart ? [] : collectBars(ctx, sessionStart);\n const snapshot = resolveVolumeProfileSnapshot({\n bars,\n bucketColor: opts?.bucketColor,\n config: volumeProfileConfigFromOpts(opts ?? {}),\n });\n commitVolumeProfileSnapshot(slot, ctx.isTick, snapshot);\n emitVolumeProfileHistogram(\n ctx,\n slotId,\n \"Session Volume Profile\",\n snapshot.poc,\n snapshot.buckets,\n );\n return resultForOffset(slot, opts?.offset ?? 0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sessionVolumeProfile.js","sourceRoot":"","sources":["../../src/ta/sessionVolumeProfile.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// Ported from ../invinite/src/components/trading-chart/indicators/session-volume-profile.ts\n// @ 3234c8c0c3f9880d9d1e3a3ee63ebd55ddd535f4.\n// Translated, not transcribed — Series<T> shape, opts.offset, JSDoc.\n// See packages/runtime/src/ta/CLAUDE.md for the port convention.\n\nimport type {\n SessionVolumeProfileOpts,\n SessionVolumeProfileResult,\n} from \"@invinite-org/chartlang-core\";\n\nimport { pushDiagnostic } from \"../emit/index.js\";\nimport { ACTIVE_RUNTIME_CONTEXT, type RuntimeContext } from \"../runtimeContext.js\";\nimport { makeSeriesView, makeShiftedSeriesView } from \"../seriesView.js\";\nimport { parseSessionWindowMinutes } from \"../time-accessors/sessionWindow.js\";\nimport {\n type VolumeProfileBar,\n type VolumeProfileCore,\n commitVolumeProfileSnapshot,\n createVolumeProfileCore,\n emitVolumeProfileHistogram,\n resolveVolumeProfileSnapshot,\n volumeProfileConfigFromOpts,\n} from \"./lib/volume-profile/index.js\";\n\nconst DAY_MS = 86_400_000;\n\ntype SessionVolumeProfileSlot = VolumeProfileCore & {\n readonly result: SessionVolumeProfileResult;\n readonly shiftedResults: Map<number, SessionVolumeProfileResult>;\n};\n\ntype MutableSessionVolumeProfileSlot = Omit<SessionVolumeProfileSlot, \"result\"> & {\n result: SessionVolumeProfileResult;\n};\n\nfunction getCtx(): RuntimeContext {\n const ctx = ACTIVE_RUNTIME_CONTEXT.current;\n if (ctx === null) {\n throw new Error(\"ta.sessionVolumeProfile called outside an active script step\");\n }\n return ctx;\n}\n\nfunction initSlot(capacity: number): SessionVolumeProfileSlot {\n const core = createVolumeProfileCore(capacity);\n const slot: MutableSessionVolumeProfileSlot = {\n ...core,\n result: Object.freeze({\n get buckets() {\n return slot.buckets;\n },\n poc: makeSeriesView<number>(core.pocBuffer),\n valHigh: makeSeriesView<number>(core.valHighBuffer),\n valLow: makeSeriesView<number>(core.valLowBuffer),\n }),\n shiftedResults: new Map(),\n };\n return slot;\n}\n\nfunction resultForOffset(\n slot: SessionVolumeProfileSlot,\n offset: number,\n): SessionVolumeProfileResult {\n if (offset === 0) return slot.result;\n let cached = slot.shiftedResults.get(offset);\n if (cached === undefined) {\n cached = Object.freeze({\n get buckets() {\n return slot.buckets;\n },\n poc: makeShiftedSeriesView<number>(slot.pocBuffer, offset),\n valHigh: makeShiftedSeriesView<number>(slot.valHighBuffer, offset),\n valLow: makeShiftedSeriesView<number>(slot.valLowBuffer, offset),\n });\n slot.shiftedResults.set(offset, cached);\n }\n return cached;\n}\n\nfunction utcDayStart(time: number): number {\n return Math.floor(time / DAY_MS) * DAY_MS;\n}\n\nfunction sessionBoundaryFromDescriptor(time: number, session: string): number | null {\n const parsed = parseSessionWindowMinutes(session);\n if (parsed === null) return null;\n const dayStart = utcDayStart(time);\n const boundary = dayStart + parsed.startMinutes * 60_000;\n return time >= boundary ? boundary : boundary - DAY_MS;\n}\n\nfunction diagnoseMissingSession(ctx: RuntimeContext, slotId: string): void {\n const key = `session-info-missing|${slotId}`;\n if (ctx.diagnosedRequestKeys.has(key)) return;\n ctx.diagnosedRequestKeys.add(key);\n pushDiagnostic(ctx.emissions, {\n kind: \"diagnostic\",\n severity: \"warning\",\n code: \"session-info-missing\",\n message:\n \"Adapter did not provide syminfo.session; ta.sessionVolumeProfile used UTC-day boundaries.\",\n slotId,\n bar: ctx.barIndex(),\n });\n}\n\nfunction resolveSessionStart(\n ctx: RuntimeContext,\n slotId: string,\n opts: SessionVolumeProfileOpts | undefined,\n): number {\n if (opts?.sessionStart !== undefined) return opts.sessionStart;\n const session = ctx.views.syminfo.session;\n if (!ctx.capabilities.symInfoFields.has(\"session\") || session === \"\") {\n diagnoseMissingSession(ctx, slotId);\n return utcDayStart(ctx.stream.bar.time);\n }\n const boundary = sessionBoundaryFromDescriptor(ctx.stream.bar.time, session);\n if (boundary === null) {\n diagnoseMissingSession(ctx, slotId);\n return utcDayStart(ctx.stream.bar.time);\n }\n return boundary;\n}\n\nfunction collectBars(ctx: RuntimeContext, sessionStart: number): ReadonlyArray<VolumeProfileBar> {\n const { ohlcv } = ctx.stream;\n const bars: VolumeProfileBar[] = [];\n for (let lookback = ohlcv.close.length - 1; lookback >= 0; lookback -= 1) {\n const time = ohlcv.time.at(lookback);\n if (time <= sessionStart) continue;\n bars.push({\n close: ohlcv.close.at(lookback),\n high: ohlcv.high.at(lookback),\n low: ohlcv.low.at(lookback),\n open: ohlcv.open.at(lookback),\n time,\n volume: ohlcv.volume.at(lookback),\n });\n }\n return bars;\n}\n\n/**\n * Session Volume Profile — bucketizes the current session's volume\n * by price, resetting when `bar.time` crosses the active session\n * boundary.\n *\n * @formula Port of invinite session-vp: find the current session\n * window, bucket volume by price, then derive POC / value-\n * area high / value-area low via the shared volume-profile\n * pipeline.\n * @anchors `syminfo.session` descriptor, or `opts.sessionStart`\n * override when provided.\n * @warmup NaN until a session window has positive volume; missing\n * `syminfo.session` falls back to UTC-day boundaries.\n * @since 0.5\n * @stable\n * @example\n * // import { plot, ta } from \"@invinite-org/chartlang-core\";\n * // const vp = ta.sessionVolumeProfile({ rowSize: 24 });\n * // plot(vp.poc, { style: { kind: \"horizontal-histogram\", buckets: vp.buckets } });\n */\nexport function sessionVolumeProfile(\n slotId: string,\n opts?: SessionVolumeProfileOpts,\n): SessionVolumeProfileResult {\n const ctx = getCtx();\n let slot = ctx.stream.taSlots.get(slotId) as SessionVolumeProfileSlot | undefined;\n if (slot === undefined) {\n slot = initSlot(ctx.stream.ohlcv.close.capacity);\n ctx.stream.taSlots.set(slotId, slot);\n }\n\n const sessionStart = resolveSessionStart(ctx, slotId, opts);\n const bars = ctx.stream.bar.time <= sessionStart ? [] : collectBars(ctx, sessionStart);\n const snapshot = resolveVolumeProfileSnapshot({\n bars,\n bucketColor: opts?.bucketColor,\n config: volumeProfileConfigFromOpts(opts ?? {}),\n });\n commitVolumeProfileSnapshot(slot, ctx.isTick, snapshot);\n emitVolumeProfileHistogram(\n ctx,\n slotId,\n \"Session Volume Profile\",\n snapshot.poc,\n snapshot.buckets,\n );\n return resultForOffset(slot, opts?.offset ?? 0);\n}\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integer floor-division rounding toward −∞ (unlike JS `/ | 0`, which
|
|
3
|
+
* truncates toward `0`). Required so pre-1970 epochs split correctly.
|
|
4
|
+
*
|
|
5
|
+
* @since 1.5
|
|
6
|
+
* @stable
|
|
7
|
+
* @example
|
|
8
|
+
* // import { floorDiv } from "@invinite-org/chartlang-runtime";
|
|
9
|
+
* // floorDiv(-1, 86_400_000); // -1, not 0
|
|
10
|
+
*/
|
|
11
|
+
export declare function floorDiv(a: number, b: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* Non-negative modulo (Euclidean) — `mod(-1, 7) === 6`, unlike JS `%`
|
|
14
|
+
* which would yield `-1`. Pairs with {@link floorDiv} for correct
|
|
15
|
+
* pre-epoch date math.
|
|
16
|
+
*
|
|
17
|
+
* @since 1.5
|
|
18
|
+
* @stable
|
|
19
|
+
* @example
|
|
20
|
+
* // import { mod } from "@invinite-org/chartlang-runtime";
|
|
21
|
+
* // mod(-1, 7); // 6
|
|
22
|
+
*/
|
|
23
|
+
export declare function mod(a: number, b: number): number;
|
|
24
|
+
/**
|
|
25
|
+
* The civil date for a count of days since the Unix epoch
|
|
26
|
+
* (1970-01-01). `m` is `1..12`, `d` is `1..31`. Pure integer
|
|
27
|
+
* arithmetic — correct for negative (pre-epoch) `z`.
|
|
28
|
+
*
|
|
29
|
+
* @since 1.5
|
|
30
|
+
* @stable
|
|
31
|
+
* @example
|
|
32
|
+
* // import { civilFromDays } from "@invinite-org/chartlang-runtime";
|
|
33
|
+
* // civilFromDays(0); // { y: 1970, m: 1, d: 1 }
|
|
34
|
+
*/
|
|
35
|
+
export declare function civilFromDays(z: number): {
|
|
36
|
+
y: number;
|
|
37
|
+
m: number;
|
|
38
|
+
d: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Days since the Unix epoch (1970-01-01) for a civil `y` / `m` (`1..12`)
|
|
42
|
+
* / `d` (`1..31`). Inverse of {@link civilFromDays}. Pure integer
|
|
43
|
+
* arithmetic — correct for pre-epoch dates.
|
|
44
|
+
*
|
|
45
|
+
* @since 1.5
|
|
46
|
+
* @stable
|
|
47
|
+
* @example
|
|
48
|
+
* // import { daysFromCivil } from "@invinite-org/chartlang-runtime";
|
|
49
|
+
* // daysFromCivil(1970, 1, 1); // 0
|
|
50
|
+
*/
|
|
51
|
+
export declare function daysFromCivil(y: number, m: number, d: number): number;
|
|
52
|
+
/**
|
|
53
|
+
* Split a UTC ms epoch (after pre-shifting by `offsetMin` minutes) into
|
|
54
|
+
* civil fields. `dow` is `0=Sun .. 6=Sat`, derived from the day count
|
|
55
|
+
* (`mod(z + 4, 7)` — 1970-01-01 was a Thursday, index 4). All integer
|
|
56
|
+
* arithmetic on the floored ms — no `Date`.
|
|
57
|
+
*
|
|
58
|
+
* @since 1.5
|
|
59
|
+
* @stable
|
|
60
|
+
* @example
|
|
61
|
+
* // import { splitEpoch } from "@invinite-org/chartlang-runtime";
|
|
62
|
+
* // splitEpoch(0, 0); // { y: 1970, m: 1, d: 1, hh: 0, mm: 0, ss: 0, dow: 4 }
|
|
63
|
+
*/
|
|
64
|
+
export declare function splitEpoch(ms: number, offsetMin: number): {
|
|
65
|
+
y: number;
|
|
66
|
+
m: number;
|
|
67
|
+
d: number;
|
|
68
|
+
hh: number;
|
|
69
|
+
mm: number;
|
|
70
|
+
ss: number;
|
|
71
|
+
dow: number;
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=civil.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"civil.d.ts","sourceRoot":"","sources":["../../src/time-accessors/civil.ts"],"names":[],"mappings":"AAUA;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAc5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACtB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,GAClB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CActF"}
|
|
@@ -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":"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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//\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
|