@matchstick-trading/primitives 0.0.10

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 (216) hide show
  1. package/AGENTS.md +44 -0
  2. package/CHANGELOG.md +19 -0
  3. package/LICENSE +201 -0
  4. package/README.md +636 -0
  5. package/dist/Chart.d.ts +18 -0
  6. package/dist/Chart.js +6 -0
  7. package/dist/ChartParts.d.ts +151 -0
  8. package/dist/ChartParts.js +389 -0
  9. package/dist/CsvDrop.d.ts +8 -0
  10. package/dist/CsvDrop.js +5 -0
  11. package/dist/CsvDropParts.d.ts +33 -0
  12. package/dist/CsvDropParts.js +77 -0
  13. package/dist/Cursor.d.ts +6 -0
  14. package/dist/Cursor.js +5 -0
  15. package/dist/CursorParts.d.ts +51 -0
  16. package/dist/CursorParts.js +40 -0
  17. package/dist/DepthProfile.d.ts +13 -0
  18. package/dist/DepthProfile.js +5 -0
  19. package/dist/DepthProfileParts.d.ts +45 -0
  20. package/dist/DepthProfileParts.js +96 -0
  21. package/dist/DiffTable.d.ts +13 -0
  22. package/dist/DiffTable.js +5 -0
  23. package/dist/DiffTableParts.d.ts +57 -0
  24. package/dist/DiffTableParts.js +103 -0
  25. package/dist/Drawings.d.ts +13 -0
  26. package/dist/Drawings.js +5 -0
  27. package/dist/DrawingsParts.d.ts +194 -0
  28. package/dist/DrawingsParts.js +775 -0
  29. package/dist/Fullscreen.d.ts +7 -0
  30. package/dist/Fullscreen.js +5 -0
  31. package/dist/FullscreenParts.d.ts +54 -0
  32. package/dist/FullscreenParts.js +250 -0
  33. package/dist/Ladder.d.ts +13 -0
  34. package/dist/Ladder.js +5 -0
  35. package/dist/LadderParts.d.ts +49 -0
  36. package/dist/LadderParts.js +56 -0
  37. package/dist/Menu.d.ts +11 -0
  38. package/dist/Menu.js +5 -0
  39. package/dist/MenuParts.d.ts +52 -0
  40. package/dist/MenuParts.js +202 -0
  41. package/dist/Notice.d.ts +18 -0
  42. package/dist/Notice.js +9 -0
  43. package/dist/Panels.d.ts +9 -0
  44. package/dist/Panels.js +5 -0
  45. package/dist/PanelsParts.d.ts +62 -0
  46. package/dist/PanelsParts.js +387 -0
  47. package/dist/Receipt.d.ts +11 -0
  48. package/dist/Receipt.js +5 -0
  49. package/dist/ReceiptParts.d.ts +45 -0
  50. package/dist/ReceiptParts.js +71 -0
  51. package/dist/Scrubber.d.ts +9 -0
  52. package/dist/Scrubber.js +5 -0
  53. package/dist/ScrubberParts.d.ts +74 -0
  54. package/dist/ScrubberParts.js +126 -0
  55. package/dist/Selection.d.ts +14 -0
  56. package/dist/Selection.js +7 -0
  57. package/dist/SelectionParts.d.ts +63 -0
  58. package/dist/SelectionParts.js +208 -0
  59. package/dist/Studies.d.ts +9 -0
  60. package/dist/Studies.js +5 -0
  61. package/dist/StudiesParts.d.ts +45 -0
  62. package/dist/StudiesParts.js +153 -0
  63. package/dist/Submenu.d.ts +7 -0
  64. package/dist/Submenu.js +4 -0
  65. package/dist/SymbolSearch.d.ts +10 -0
  66. package/dist/SymbolSearch.js +5 -0
  67. package/dist/SymbolSearchParts.d.ts +39 -0
  68. package/dist/SymbolSearchParts.js +86 -0
  69. package/dist/Tape.d.ts +15 -0
  70. package/dist/Tape.js +5 -0
  71. package/dist/TapeParts.d.ts +69 -0
  72. package/dist/TapeParts.js +139 -0
  73. package/dist/Toolbar.d.ts +8 -0
  74. package/dist/Toolbar.js +5 -0
  75. package/dist/ToolbarParts.d.ts +61 -0
  76. package/dist/ToolbarParts.js +122 -0
  77. package/dist/Transport.d.ts +15 -0
  78. package/dist/Transport.js +5 -0
  79. package/dist/TransportParts.d.ts +52 -0
  80. package/dist/TransportParts.js +47 -0
  81. package/dist/VolumeProfile.d.ts +7 -0
  82. package/dist/VolumeProfile.js +5 -0
  83. package/dist/VolumeProfileParts.d.ts +37 -0
  84. package/dist/VolumeProfileParts.js +57 -0
  85. package/dist/accessible-name.d.ts +26 -0
  86. package/dist/accessible-name.js +14 -0
  87. package/dist/attributes.d.ts +8 -0
  88. package/dist/attributes.js +9 -0
  89. package/dist/core/adapter.d.ts +38 -0
  90. package/dist/core/adapter.js +64 -0
  91. package/dist/core/attributes.d.ts +36 -0
  92. package/dist/core/attributes.js +22 -0
  93. package/dist/core/candles.d.ts +6 -0
  94. package/dist/core/candles.js +19 -0
  95. package/dist/core/chart-gesture.d.ts +109 -0
  96. package/dist/core/chart-gesture.js +161 -0
  97. package/dist/core/chart-scale.d.ts +141 -0
  98. package/dist/core/chart-scale.js +302 -0
  99. package/dist/core/csv-bars.d.ts +54 -0
  100. package/dist/core/csv-bars.js +325 -0
  101. package/dist/core/depth-profile.d.ts +43 -0
  102. package/dist/core/depth-profile.js +66 -0
  103. package/dist/core/diff-table.d.ts +57 -0
  104. package/dist/core/diff-table.js +77 -0
  105. package/dist/core/drawing-fields.d.ts +11 -0
  106. package/dist/core/drawing-fields.js +15 -0
  107. package/dist/core/drawings.d.ts +194 -0
  108. package/dist/core/drawings.js +403 -0
  109. package/dist/core/fullscreen.d.ts +32 -0
  110. package/dist/core/fullscreen.js +16 -0
  111. package/dist/core/index.d.ts +41 -0
  112. package/dist/core/index.js +23 -0
  113. package/dist/core/ladder-keys.d.ts +35 -0
  114. package/dist/core/ladder-keys.js +43 -0
  115. package/dist/core/ladder.d.ts +65 -0
  116. package/dist/core/ladder.js +93 -0
  117. package/dist/core/panels.d.ts +72 -0
  118. package/dist/core/panels.js +151 -0
  119. package/dist/core/receipt-rules.d.ts +42 -0
  120. package/dist/core/receipt-rules.js +20 -0
  121. package/dist/core/scrubber-gesture.d.ts +66 -0
  122. package/dist/core/scrubber-gesture.js +69 -0
  123. package/dist/core/selection.d.ts +24 -0
  124. package/dist/core/selection.js +33 -0
  125. package/dist/core/series.d.ts +161 -0
  126. package/dist/core/series.js +331 -0
  127. package/dist/core/studies.d.ts +80 -0
  128. package/dist/core/studies.js +237 -0
  129. package/dist/core/symbol-search.d.ts +89 -0
  130. package/dist/core/symbol-search.js +223 -0
  131. package/dist/core/tape.d.ts +77 -0
  132. package/dist/core/tape.js +136 -0
  133. package/dist/core/volume-profile.d.ts +47 -0
  134. package/dist/core/volume-profile.js +86 -0
  135. package/dist/index.d.ts +82 -0
  136. package/dist/index.js +42 -0
  137. package/dist/ladder-keys.d.ts +16 -0
  138. package/dist/ladder-keys.js +86 -0
  139. package/dist/panels-context.d.ts +47 -0
  140. package/dist/panels-context.js +3 -0
  141. package/dist/selection-context.d.ts +41 -0
  142. package/dist/selection-context.js +4 -0
  143. package/examples/csv-to-chart-frame.mjs +17 -0
  144. package/examples/studies.mjs +12 -0
  145. package/llms.txt +16 -0
  146. package/package.json +67 -0
  147. package/src/Chart.tsx +7 -0
  148. package/src/ChartParts.tsx +529 -0
  149. package/src/CsvDrop.tsx +6 -0
  150. package/src/CsvDropParts.tsx +128 -0
  151. package/src/Cursor.tsx +6 -0
  152. package/src/CursorParts.tsx +77 -0
  153. package/src/DepthProfile.tsx +6 -0
  154. package/src/DepthProfileParts.tsx +154 -0
  155. package/src/DiffTable.tsx +6 -0
  156. package/src/DiffTableParts.tsx +160 -0
  157. package/src/Drawings.tsx +6 -0
  158. package/src/DrawingsParts.tsx +1080 -0
  159. package/src/Fullscreen.tsx +6 -0
  160. package/src/FullscreenParts.tsx +318 -0
  161. package/src/Ladder.tsx +6 -0
  162. package/src/LadderParts.tsx +114 -0
  163. package/src/Menu.tsx +6 -0
  164. package/src/MenuParts.tsx +364 -0
  165. package/src/Notice.tsx +27 -0
  166. package/src/Panels.tsx +6 -0
  167. package/src/PanelsParts.tsx +454 -0
  168. package/src/Receipt.tsx +6 -0
  169. package/src/ReceiptParts.tsx +118 -0
  170. package/src/Scrubber.tsx +6 -0
  171. package/src/ScrubberParts.tsx +207 -0
  172. package/src/Selection.tsx +12 -0
  173. package/src/SelectionParts.tsx +251 -0
  174. package/src/Studies.tsx +6 -0
  175. package/src/StudiesParts.tsx +226 -0
  176. package/src/Submenu.tsx +5 -0
  177. package/src/SymbolSearch.tsx +6 -0
  178. package/src/SymbolSearchParts.tsx +166 -0
  179. package/src/Tape.tsx +6 -0
  180. package/src/TapeParts.tsx +208 -0
  181. package/src/Toolbar.tsx +6 -0
  182. package/src/ToolbarParts.tsx +179 -0
  183. package/src/Transport.tsx +6 -0
  184. package/src/TransportParts.tsx +128 -0
  185. package/src/VolumeProfile.tsx +6 -0
  186. package/src/VolumeProfileParts.tsx +106 -0
  187. package/src/accessible-name.tsx +34 -0
  188. package/src/attributes.ts +12 -0
  189. package/src/core/adapter.ts +91 -0
  190. package/src/core/attributes.ts +51 -0
  191. package/src/core/candles.ts +22 -0
  192. package/src/core/chart-gesture.ts +228 -0
  193. package/src/core/chart-scale.ts +368 -0
  194. package/src/core/csv-bars.ts +328 -0
  195. package/src/core/depth-profile.ts +84 -0
  196. package/src/core/diff-table.ts +105 -0
  197. package/src/core/drawing-fields.ts +17 -0
  198. package/src/core/drawings.ts +478 -0
  199. package/src/core/fullscreen.ts +32 -0
  200. package/src/core/index.ts +42 -0
  201. package/src/core/ladder-keys.ts +61 -0
  202. package/src/core/ladder.ts +156 -0
  203. package/src/core/panels.ts +177 -0
  204. package/src/core/receipt-rules.ts +45 -0
  205. package/src/core/scrubber-gesture.ts +102 -0
  206. package/src/core/selection.ts +41 -0
  207. package/src/core/series.ts +374 -0
  208. package/src/core/studies.ts +260 -0
  209. package/src/core/symbol-search.ts +256 -0
  210. package/src/core/tape.ts +159 -0
  211. package/src/core/volume-profile.ts +116 -0
  212. package/src/index.ts +83 -0
  213. package/src/ladder-keys.ts +83 -0
  214. package/src/panels-context.ts +46 -0
  215. package/src/selection-context.ts +38 -0
  216. package/src/tokens.css +235 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The scrub gesture as plain data in, plain data out. Everything here works on a fraction of the
3
+ * track, 0 to 1, and knows nothing about time, sequences or events: the host turns a committed fraction into a
4
+ * place in its session. That keeps the rules testable without a DOM and keeps the Scrubber usable on any ruler.
5
+ */
6
+ /** Clamp a track fraction to [0, 1]; a value that is not a finite number becomes 0. */
7
+ export declare function clampFraction(value: number): number;
8
+ export type ScrubGestureEvent = {
9
+ type: 'down';
10
+ fraction: number;
11
+ } | {
12
+ type: 'move';
13
+ fraction: number;
14
+ }
15
+ /**
16
+ * `fraction` is where the release itself landed: pointer input coalesces, so the release can be measurably ahead
17
+ * of the last move. When it is left out the last previewed fraction is used. `loading` is whether the host is
18
+ * already busy with another navigation at the moment of release, passed in as data so the decision lives here.
19
+ */
20
+ | {
21
+ type: 'up';
22
+ fraction?: number;
23
+ loading?: boolean;
24
+ } | {
25
+ type: 'cancel';
26
+ };
27
+ export interface ScrubGestureResult {
28
+ /** The next preview fraction; null when no drag is in progress. */
29
+ dragFraction: number | null;
30
+ /** Non-null exactly once per gesture: the fraction to commit when the pointer is released. */
31
+ commit: number | null;
32
+ begin: boolean;
33
+ end: boolean;
34
+ }
35
+ /**
36
+ * Moving only ever changes the preview; only a release, with a drag in progress, commits, and it commits once.
37
+ * A release while the host is loading ends the gesture without committing, so two navigations are never in
38
+ * flight at once. A cancelled gesture (the pointer left the window) never commits.
39
+ */
40
+ export declare function reduceScrubGesture(event: ScrubGestureEvent, dragFraction: number | null): ScrubGestureResult;
41
+ /** The two pointer fields a scrub gate reads, named so the gates are testable without a DOM event. */
42
+ export interface ScrubPointer {
43
+ /** 0 is the primary button (left, or a pen or touch contact). */
44
+ button: number;
45
+ /** False for any additional simultaneous contact in a multi-touch gesture. */
46
+ isPrimary: boolean;
47
+ }
48
+ /** Whether a pointer-down may start a scrub. A right-click, middle-click or second finger is left to the browser. */
49
+ export declare function isPrimaryScrubPointer(event: ScrubPointer): boolean;
50
+ /** Whether a move, up or cancel belongs to the pointer that began the drag now in progress (a second contact cannot hijack it). */
51
+ export declare function isActiveScrubPointer(pointerId: number, activePointerId: number | null): boolean;
52
+ /** Where a pointer sits along a track, as a fraction. A track with no width reads as 0. */
53
+ export declare function fractionFromPointer(clientX: number, left: number, width: number): number;
54
+ export type ScrubKeyAction = {
55
+ type: 'step';
56
+ direction: -1 | 1;
57
+ } | {
58
+ type: 'jump';
59
+ fraction: number;
60
+ };
61
+ /**
62
+ * Arrow keys step one event, which only the host can define, so they resolve to a direction. Home and End jump to
63
+ * the ends; PageUp and PageDown move 10% of the track. Any other key is left alone (null).
64
+ */
65
+ export declare function resolveScrubKey(key: string, currentFraction: number): ScrubKeyAction | null;
66
+ //# sourceMappingURL=scrubber-gesture.d.ts.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The scrub gesture as plain data in, plain data out. Everything here works on a fraction of the
3
+ * track, 0 to 1, and knows nothing about time, sequences or events: the host turns a committed fraction into a
4
+ * place in its session. That keeps the rules testable without a DOM and keeps the Scrubber usable on any ruler.
5
+ */
6
+ /** Clamp a track fraction to [0, 1]; a value that is not a finite number becomes 0. */
7
+ export function clampFraction(value) {
8
+ if (!Number.isFinite(value))
9
+ return 0;
10
+ return value < 0 ? 0 : value > 1 ? 1 : value;
11
+ }
12
+ const IDLE = { commit: null, begin: false, end: false };
13
+ /**
14
+ * Moving only ever changes the preview; only a release, with a drag in progress, commits, and it commits once.
15
+ * A release while the host is loading ends the gesture without committing, so two navigations are never in
16
+ * flight at once. A cancelled gesture (the pointer left the window) never commits.
17
+ */
18
+ export function reduceScrubGesture(event, dragFraction) {
19
+ switch (event.type) {
20
+ case 'down':
21
+ return { dragFraction: clampFraction(event.fraction), commit: null, begin: true, end: false };
22
+ case 'move':
23
+ return dragFraction === null ? { dragFraction, ...IDLE } : { dragFraction: clampFraction(event.fraction), ...IDLE };
24
+ case 'up':
25
+ if (dragFraction === null)
26
+ return { dragFraction: null, ...IDLE };
27
+ if (event.loading)
28
+ return { dragFraction: null, commit: null, begin: false, end: true };
29
+ return { dragFraction: null, commit: clampFraction(event.fraction ?? dragFraction), begin: false, end: true };
30
+ case 'cancel':
31
+ return dragFraction === null ? { dragFraction: null, ...IDLE } : { dragFraction: null, commit: null, begin: false, end: true };
32
+ }
33
+ }
34
+ /** Whether a pointer-down may start a scrub. A right-click, middle-click or second finger is left to the browser. */
35
+ export function isPrimaryScrubPointer(event) {
36
+ return event.button === 0 && event.isPrimary;
37
+ }
38
+ /** Whether a move, up or cancel belongs to the pointer that began the drag now in progress (a second contact cannot hijack it). */
39
+ export function isActiveScrubPointer(pointerId, activePointerId) {
40
+ return activePointerId !== null && pointerId === activePointerId;
41
+ }
42
+ /** Where a pointer sits along a track, as a fraction. A track with no width reads as 0. */
43
+ export function fractionFromPointer(clientX, left, width) {
44
+ if (!(width > 0))
45
+ return 0;
46
+ return clampFraction((clientX - left) / width);
47
+ }
48
+ /**
49
+ * Arrow keys step one event, which only the host can define, so they resolve to a direction. Home and End jump to
50
+ * the ends; PageUp and PageDown move 10% of the track. Any other key is left alone (null).
51
+ */
52
+ export function resolveScrubKey(key, currentFraction) {
53
+ switch (key) {
54
+ case 'ArrowLeft':
55
+ return { type: 'step', direction: -1 };
56
+ case 'ArrowRight':
57
+ return { type: 'step', direction: 1 };
58
+ case 'Home':
59
+ return { type: 'jump', fraction: 0 };
60
+ case 'End':
61
+ return { type: 'jump', fraction: 1 };
62
+ case 'PageUp':
63
+ return { type: 'jump', fraction: clampFraction(currentFraction + 0.1) };
64
+ case 'PageDown':
65
+ return { type: 'jump', fraction: clampFraction(currentFraction - 0.1) };
66
+ default:
67
+ return null;
68
+ }
69
+ }
@@ -0,0 +1,24 @@
1
+ /** What is selected on a chart, or null for nothing. */
2
+ export type ChartSelection = {
3
+ kind: 'drawing' | 'series' | 'study';
4
+ id: string;
5
+ } | null;
6
+ /** True when two selections point at the same thing. */
7
+ export declare function sameSelection(a: ChartSelection, b: ChartSelection): boolean;
8
+ /**
9
+ * A tap on clear chart (no drawing, no study under it). With something selected, it only puts the selection away. With
10
+ * nothing selected it selects the series when one is mounted (`seriesId`), and otherwise changes nothing. Returns the
11
+ * same value when nothing changes.
12
+ */
13
+ export declare function selectionAfterClearTap(selection: ChartSelection, seriesId: string | null): ChartSelection;
14
+ /** A tap on something selectable (a study line) selects it, whatever was selected before. */
15
+ export declare function selectionAfterTap(selection: ChartSelection, hit: ChartSelection, seriesId: string | null): ChartSelection;
16
+ /** Escape clears whatever is selected. */
17
+ export declare function selectionAfterEscape(selection: ChartSelection): ChartSelection;
18
+ /** The names a selection can be described with, per kind and id. A missing name falls back to the id. */
19
+ export type SelectionNames = {
20
+ [K in 'drawing' | 'series' | 'study']?: Readonly<Record<string, string>>;
21
+ };
22
+ /** The words for a live region: what is selected now, or that nothing is. */
23
+ export declare function describeSelection(selection: ChartSelection, names?: SelectionNames): string;
24
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1,33 @@
1
+ // One selection for a chart: a drawing, the bar series or a study, never more than one. The rules are plain functions
2
+ // so any framework layer (and a test) can use them.
3
+ /** True when two selections point at the same thing. */
4
+ export function sameSelection(a, b) {
5
+ return a === b || (a !== null && b !== null && a.kind === b.kind && a.id === b.id);
6
+ }
7
+ /**
8
+ * A tap on clear chart (no drawing, no study under it). With something selected, it only puts the selection away. With
9
+ * nothing selected it selects the series when one is mounted (`seriesId`), and otherwise changes nothing. Returns the
10
+ * same value when nothing changes.
11
+ */
12
+ export function selectionAfterClearTap(selection, seriesId) {
13
+ if (selection !== null)
14
+ return null;
15
+ return seriesId !== null ? { kind: 'series', id: seriesId } : selection;
16
+ }
17
+ /** A tap on something selectable (a study line) selects it, whatever was selected before. */
18
+ export function selectionAfterTap(selection, hit, seriesId) {
19
+ if (hit === null)
20
+ return selectionAfterClearTap(selection, seriesId);
21
+ return sameSelection(selection, hit) ? selection : hit;
22
+ }
23
+ /** Escape clears whatever is selected. */
24
+ export function selectionAfterEscape(selection) {
25
+ return selection === null ? selection : null;
26
+ }
27
+ /** The words for a live region: what is selected now, or that nothing is. */
28
+ export function describeSelection(selection, names = {}) {
29
+ if (selection === null)
30
+ return 'Selection cleared.';
31
+ const name = names[selection.kind]?.[selection.id] ?? selection.id;
32
+ return `Selected ${selection.kind}: ${name}.`;
33
+ }
@@ -0,0 +1,161 @@
1
+ /** Why a point has no number. `value` has one. */
2
+ export type PointKind = 'value' | 'missing' | 'market-closed' | 'not-applicable' | 'halted' | 'unknown';
3
+ /** One point on a series. `t` is ISO-8601 UTC, for example `2026-01-05T14:30:00Z`. Only `value` carries `v`. */
4
+ export type SeriesPoint = {
5
+ t: string;
6
+ kind: 'value';
7
+ v: number;
8
+ } | {
9
+ t: string;
10
+ kind: Exclude<PointKind, 'value'>;
11
+ };
12
+ /** Which calendar decided `market-closed`. A label: the host fills it and nothing here resolves it. */
13
+ export interface SeriesCalendar {
14
+ id: string;
15
+ version?: string;
16
+ tz: string;
17
+ }
18
+ export interface DataSeries {
19
+ id: string;
20
+ label: string;
21
+ unit?: string;
22
+ calendar?: SeriesCalendar;
23
+ /** Strictly increasing in time. */
24
+ points: SeriesPoint[];
25
+ }
26
+ /** `exact`: same instant. `asof`: the latest point at or before, within `toleranceMs`. `none`: no cross-series comparison. */
27
+ export type AlignMode = 'exact' | 'asof' | 'none';
28
+ export interface SeriesSet {
29
+ schemaVersion: '0';
30
+ alignment: {
31
+ mode: AlignMode;
32
+ toleranceMs?: number;
33
+ primary: string;
34
+ };
35
+ /** Half-open `[start, end)`: a point at `end` is outside it. */
36
+ window?: {
37
+ start: string;
38
+ end: string;
39
+ };
40
+ series: DataSeries[];
41
+ }
42
+ export declare const POINT_KINDS: readonly PointKind[];
43
+ /** One problem found at the boundary, with the path to it (`series.1.points.3.t`). */
44
+ export interface SeriesIssue {
45
+ path: (string | number)[];
46
+ message: string;
47
+ }
48
+ export type SeriesValidation = {
49
+ ok: true;
50
+ value: SeriesSet;
51
+ } | {
52
+ ok: false;
53
+ issues: SeriesIssue[];
54
+ };
55
+ /**
56
+ * Check a value at the boundary (a recorded session, an adapter's output, a hand-built set). Every problem is returned
57
+ * in one pass, not only the first. The input is not changed.
58
+ */
59
+ export declare function validateSeriesSet(input: unknown): SeriesValidation;
60
+ /** The outcome shape Standard Schema v1 uses, kept local so the core imports nothing. */
61
+ type StandardResult<T> = {
62
+ value: T;
63
+ issues?: undefined;
64
+ } | {
65
+ issues: ReadonlyArray<{
66
+ message: string;
67
+ path?: ReadonlyArray<string | number>;
68
+ }>;
69
+ value?: undefined;
70
+ };
71
+ /**
72
+ * `validateSeriesSet` as a Standard Schema (v1), so any validator-aware tool can run it at a boundary, for example
73
+ * `check(seriesSetSchema, data)` from `@fieldtest/core`. Imports nothing.
74
+ */
75
+ export declare const seriesSetSchema: {
76
+ readonly '~standard': {
77
+ readonly version: 1;
78
+ readonly vendor: 'matchstick-primitives';
79
+ readonly validate: (value: unknown) => StandardResult<SeriesSet>;
80
+ readonly types?: {
81
+ readonly input: SeriesSet;
82
+ readonly output: SeriesSet;
83
+ };
84
+ };
85
+ };
86
+ /** What `report` finds. It reads and never repairs. */
87
+ export type FindingKind = 'gap-kind-differs' | 'no-point' | 'feed-stopped' | 'unclassified';
88
+ export interface SeriesFinding {
89
+ /** An ISO time, on the primary's grid for the comparison findings. */
90
+ t: string;
91
+ kind: FindingKind;
92
+ /** The series involved: the primary first, then the other, for a comparison. */
93
+ series: string[];
94
+ detail: string;
95
+ }
96
+ /**
97
+ * Where the series in a set disagree, read on the primary's timestamps. In order: every `unknown` point in the window
98
+ * (`unclassified`), a series that stops before the window ends (`feed-stopped`), then for each primary point and
99
+ * each other series, no point within tolerance (`no-point`) or two different kinds (`gap-kind-differs`).
100
+ *
101
+ * `not-applicable` against anything is expected (a warm-up) and is not reported. `unknown` against another gap kind
102
+ * cannot disagree, but `unknown` against a value does. `halted` is an ordinary kind. With mode `none` there is no
103
+ * comparison across series. The set is not changed; run `validateSeriesSet` first for a set from outside.
104
+ */
105
+ export declare function report(set: SeriesSet): SeriesFinding[];
106
+ /** What a series is at one bar: a number, a kind of nothing, or `no-point` when the series has no point there. */
107
+ export type AlignedState = PointKind | 'no-point';
108
+ export interface AlignedSeries {
109
+ id: string;
110
+ label: string;
111
+ /** One entry per bar; `null` for any bar without a finite value. Zero stays zero. */
112
+ values: Array<number | null>;
113
+ /** One entry per bar: why `values` is null there, or `value`. */
114
+ states: AlignedState[];
115
+ }
116
+ export interface AlignOptions {
117
+ mode?: AlignMode;
118
+ /** For `asof`: how far back a point may be and still count. Milliseconds. */
119
+ toleranceMs?: number;
120
+ }
121
+ /**
122
+ * Put a series on the bars' clock: for each bar time, the point at that instant (`exact`) or the latest point at or
123
+ * before it within the tolerance (`asof`). A bar with no point gets `null` and the state `no-point`. Nothing is carried
124
+ * across a gap kind and no zero is invented. `none` is read as `exact`.
125
+ */
126
+ export declare function alignSeries(barTimes: readonly number[], series: DataSeries, options?: AlignOptions): AlignedSeries;
127
+ /**
128
+ * Turn a value-per-bar array (a study, or anything made by position) into points on the bars' clock. A number is a
129
+ * value; `null`, NaN and infinity become `gapKind` (default `not-applicable`, which is what a warm-up is).
130
+ */
131
+ export declare function pointsFromValues(barTimes: ReadonlyArray<number | {
132
+ timestamp: number;
133
+ }>, values: ReadonlyArray<number | null | undefined>, gapKind?: Exclude<PointKind, 'value'>): SeriesPoint[];
134
+ /** A series as a part receives it: on its own clock (`points`), or by position (`values`, the shim). */
135
+ export interface SeriesInput {
136
+ id: string;
137
+ label: string;
138
+ points?: ReadonlyArray<SeriesPoint>;
139
+ /** Kept for now so existing code keeps drawing: one value per bar by position. It will be removed in a later version. */
140
+ values?: ReadonlyArray<number | null>;
141
+ }
142
+ export interface ResolvedSeries {
143
+ /** One entry per bar; `null` for none. */
144
+ values: ReadonlyArray<number | null>;
145
+ /** Why a bar has no value; absent for the position shim, which cannot say. */
146
+ states?: AlignedState[];
147
+ /** Plain sentences about what is wrong with the input, empty when nothing is. Nothing is repaired. */
148
+ problems: string[];
149
+ }
150
+ /**
151
+ * The values to draw for one series on these bars. `points` are placed by time (see `alignSeries`); `values` are read by
152
+ * position. Problems are returned in words and the series is still drawn as far as it can be: a length that does not
153
+ * match the bars, a series with no value on any bar, points out of time order.
154
+ */
155
+ export declare function resolveSeries(barTimes: readonly number[], input: SeriesInput, options?: AlignOptions): ResolvedSeries;
156
+ /** The ids used by more than one series, once each. */
157
+ export declare function duplicateIds(items: ReadonlyArray<{
158
+ id: string;
159
+ }>): string[];
160
+ export {};
161
+ //# sourceMappingURL=series.d.ts.map