@kymoscope/sdk 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/assets/{importer.worker-Drm9u2Rc.js → importer.worker-CLBGjz4d.js} +1 -1
  2. package/dist/assets/{offscreenLineWorker-mShhnyXX.js → offscreenLineWorker-BLxay-Po.js} +2 -2
  3. package/dist/importers-B6rkbTwQ.js +5 -0
  4. package/dist/keys-KE0GTKAF.js +10 -0
  5. package/dist/kymoscope-embed.js +91 -90
  6. package/dist/kymoscope-sdk.js +86 -84
  7. package/dist/rules/kyr1.schema.json +135 -0
  8. package/dist/rules.js +2 -0
  9. package/dist/styles/charts.css +191 -0
  10. package/dist/threshold-C9it0Y7P.js +2 -0
  11. package/dist/types/charts/AnnotationLayer.d.ts +7 -2
  12. package/dist/types/charts/CameraPanel.d.ts +43 -0
  13. package/dist/types/charts/ChartBody.d.ts +12 -0
  14. package/dist/types/charts/beacon.d.ts +4 -1
  15. package/dist/types/charts/cameraLayout.d.ts +10 -0
  16. package/dist/types/charts/liveOverlay.d.ts +14 -0
  17. package/dist/types/charts/runtime.d.ts +12 -1
  18. package/dist/types/charts/useWorksheetData.d.ts +1 -5
  19. package/dist/types/components/GaugeChart.d.ts +13 -1
  20. package/dist/types/components/MultiBandCanvasChart.d.ts +4 -2
  21. package/dist/types/components/dashboardGridLayout.d.ts +1 -1
  22. package/dist/types/components/multiBandHover.d.ts +7 -0
  23. package/dist/types/engine/alerts.d.ts +56 -0
  24. package/dist/types/engine/columns.d.ts +16 -0
  25. package/dist/types/engine/createEngine.d.ts +60 -2
  26. package/dist/types/engine/derive.d.ts +1 -1
  27. package/dist/types/engine/errors.d.ts +1 -1
  28. package/dist/types/engine/liveController.d.ts +11 -1
  29. package/dist/types/engine/types.d.ts +41 -2
  30. package/dist/types/index.d.ts +26 -3
  31. package/dist/types/lib/annotations.d.ts +20 -0
  32. package/dist/types/lib/beacon.d.ts +2 -1
  33. package/dist/types/lib/data/columnarDataSource.d.ts +1 -0
  34. package/dist/types/lib/data/dataSource.d.ts +3 -0
  35. package/dist/types/lib/data/duckDbDataSource.d.ts +1 -0
  36. package/dist/types/lib/data/historyDataSource.d.ts +51 -0
  37. package/dist/types/lib/data/inMemoryDataSource.d.ts +1 -0
  38. package/dist/types/lib/data/liveDataSource.d.ts +21 -2
  39. package/dist/types/lib/data/pointColumns.d.ts +11 -0
  40. package/dist/types/lib/duckdb/duckdbInstance.d.ts +5 -0
  41. package/dist/types/lib/historian/blockCache.d.ts +32 -0
  42. package/dist/types/lib/historian/export.d.ts +31 -0
  43. package/dist/types/lib/historian/khd1.d.ts +41 -0
  44. package/dist/types/lib/historian/parquet.d.ts +21 -0
  45. package/dist/types/lib/historian/reader.d.ts +73 -0
  46. package/dist/types/lib/live/blockWire.d.ts +16 -0
  47. package/dist/types/lib/live/buffer.d.ts +36 -1
  48. package/dist/types/lib/live/cameraSender.d.ts +70 -0
  49. package/dist/types/lib/live/clockMap.d.ts +20 -0
  50. package/dist/types/lib/live/crypto.d.ts +7 -0
  51. package/dist/types/lib/live/publisher.d.ts +163 -0
  52. package/dist/types/lib/live/types.d.ts +19 -1
  53. package/dist/types/lib/live/videoTrack.d.ts +96 -0
  54. package/dist/types/lib/live/videoWire.d.ts +35 -0
  55. package/dist/types/lib/live/wire.d.ts +4 -0
  56. package/dist/types/lib/panelChartSettings.d.ts +43 -1
  57. package/dist/types/lib/recordings/export.d.ts +1 -0
  58. package/dist/types/lib/recordings/videoSource.d.ts +56 -0
  59. package/dist/types/lib/render/alertGlyph.d.ts +7 -0
  60. package/dist/types/lib/render/glSeries.d.ts +1 -1
  61. package/dist/types/lib/segments/align.d.ts +85 -0
  62. package/dist/types/lib/segments/liveAxis.d.ts +58 -0
  63. package/dist/types/lib/segments/rules.d.ts +77 -0
  64. package/dist/types/lib/workspaces/io.d.ts +6 -0
  65. package/dist/types/lib/yAxisTicks.d.ts +11 -0
  66. package/dist/types/rules/compile.d.ts +47 -0
  67. package/dist/types/rules/evaluator.d.ts +51 -0
  68. package/dist/types/rules/index.d.ts +4 -0
  69. package/dist/types/rules/threshold.d.ts +3 -0
  70. package/dist/types/rules/types.d.ts +89 -0
  71. package/dist/types/rules/validate.d.ts +6 -0
  72. package/dist/types/types/loadedDataset.d.ts +5 -0
  73. package/dist/types/types/plotSeries.d.ts +5 -0
  74. package/package.json +7 -2
  75. package/dist/importers-N1JOOVEg.js +0 -5
  76. package/dist/krc-D_CI7AI8.js +0 -9
@@ -1,4 +1,11 @@
1
1
  import type { PointColumns } from '../lib/data/pointColumns';
2
+ /**
3
+ * Sends the hovered X (or null) at most once per animation frame, the newest
4
+ * value winning. With no frame scheduler (no window: tests, node) it sends at once.
5
+ */
6
+ export declare function createCursorEmitter(send: (x: number | null) => void, schedule?: ((cb: () => void) => void) | null): {
7
+ set(x: number | null): void;
8
+ };
2
9
  /** X in data units under a cursor pixel, for the current view. */
3
10
  export declare function dataXAtPx(px: number, left: number, plotW: number, xmin: number, xmax: number): number;
4
11
  /**
@@ -0,0 +1,56 @@
1
+ import { type AlertTransition, type LiveBlock, type RuleTarget } from '../rules/evaluator';
2
+ import { type RuleProblem } from '../rules/validate';
3
+ import type { RuleDoc, Severity } from '../rules/types';
4
+ /** A span on the charts from one rule episode (read-only; never stored in chart settings). t1 null: still open. */
5
+ export interface ChartMark {
6
+ id: string;
7
+ ruleId: string;
8
+ ruleName: string;
9
+ severity: Severity;
10
+ targetId: string;
11
+ timeColumn: string;
12
+ t0: number;
13
+ t1: number | null;
14
+ }
15
+ export interface RuleStateInfo {
16
+ ruleId: string;
17
+ target: RuleTarget;
18
+ state: string;
19
+ missing?: string[];
20
+ since?: number;
21
+ }
22
+ export interface AlertsHost {
23
+ /** Targets now open, with their channel names. */
24
+ targets(): Array<RuleTarget & {
25
+ channels: string[];
26
+ }>;
27
+ maxRules(): number | null;
28
+ emit(t: AlertTransition): void;
29
+ limit(): void;
30
+ setMarks(marks: ChartMark[]): void;
31
+ setStates(s: RuleStateInfo[]): void;
32
+ /** DataSource.readColumns of a loaded dataset. */
33
+ readDataset(id: string, columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
34
+ }
35
+ export declare function createAlerts(host: AlertsHost): {
36
+ /** Replace the rule set (the host's whole list). Returns the problems of the refused rules. */
37
+ setRules(docs: RuleDoc[]): Array<RuleProblem & {
38
+ ruleId: string;
39
+ }>;
40
+ rules: () => RuleDoc[];
41
+ state: () => RuleStateInfo[];
42
+ /** Evaluate the rules over a loaded dataset from its first row (fresh evaluators: "Test on loaded data"). */
43
+ evaluate(datasetId: string, o: {
44
+ timeColumn: string;
45
+ ruleIds?: string[];
46
+ }): Promise<AlertTransition[]>;
47
+ /** Live: called by the live controller with the newly appended rows of a stream. */
48
+ pushLive(streamId: string, block: LiveBlock): void;
49
+ tickLive(streamId: string, t: number): void;
50
+ /** The channel names the rules bound on a stream use (what pushLive needs from the buffer). */
51
+ liveColumns(streamId: string): string[];
52
+ /** The host's targets or their channel sets changed (a dataset loaded, a stream's schema grew). */
53
+ sync: () => void;
54
+ clearMarks(targetId?: string): void;
55
+ };
56
+ export type EngineAlerts = ReturnType<typeof createAlerts>;
@@ -1,4 +1,5 @@
1
1
  import type { LoadedDataset } from '../types/loadedDataset';
2
+ import { type HistoryPiece } from '../lib/data/historyDataSource';
2
3
  /** Columnar numeric data handed over by the host. */
3
4
  export interface ColumnsInput {
4
5
  name: string;
@@ -11,6 +12,11 @@ export interface ColumnsInput {
11
12
  * times a second, where the copy is most of the cost.
12
13
  */
13
14
  transfer?: boolean;
15
+ /** Bands to shade under columns (a reference's envelope, Historian phase 5): edges aligned with the columns' rows. */
16
+ bands?: Record<string, {
17
+ lo: ArrayLike<number>;
18
+ hi: ArrayLike<number>;
19
+ }>;
14
20
  }
15
21
  /**
16
22
  * Above this many rows host columns stay columnar (no row objects at all):
@@ -19,6 +25,8 @@ export interface ColumnsInput {
19
25
  * so laps and math channels see every row.
20
26
  */
21
27
  export declare const COLUMNAR_ROWS = 1000000;
28
+ /** Rows kept from a columnar dataset for the row-based charts (scatter, histogram, …). */
29
+ export declare const COLUMNAR_SAMPLE_ROWS = 12000;
22
30
  /** Build a dataset from host-provided numeric columns (each is copied once). */
23
31
  export declare function datasetFromColumns(id: string, input: ColumnsInput): LoadedDataset;
24
32
  /**
@@ -42,3 +50,11 @@ export declare function replaceColumns(ds: LoadedDataset, input: Record<string,
42
50
  * (cached), never on load or on each replaceColumns.
43
51
  */
44
52
  export declare function columnsText(ds: LoadedDataset): string;
53
+ /** A Historian range handed to the engine (Historian phase 4): pieces from readHistoryRange and the channels' units. */
54
+ export interface HistoryRangeView {
55
+ name: string;
56
+ pieces: HistoryPiece[];
57
+ units?: Record<string, string>;
58
+ }
59
+ /** A Historian range as a dataset: read through HistoryDataSource; its rows are a time-ordered sample. */
60
+ export declare function historyDataset(id: string, view: HistoryRangeView): LoadedDataset;
@@ -13,6 +13,7 @@ import { type PanelChartSettings } from '../lib/panelChartSettings';
13
13
  import type { MathChannel, MathChannelPatch } from '../lib/mathChannels/types';
14
14
  import type { EvalResult } from '../lib/mathChannels/types';
15
15
  import type { LiveConnection, LiveStreamDescriptor, LiveStreamOptions, LocalLiveStream, StreamKeyProvider } from '../lib/live/types';
16
+ import { type VideoSource } from '../lib/live/videoTrack';
16
17
  import type { ChannelChooser } from '../lib/importers/types';
17
18
  import type { RecordingKey } from '../lib/recordings/keys';
18
19
  import { DATASET_PALETTE } from '../lib/datasetColors';
@@ -23,12 +24,16 @@ import { type WorkbookFileOptions } from '../lib/workspaces/encrypted';
23
24
  import { canPickDirectory, isImportable, pickDirectoryEntries, type DirNode, type FolderEntry } from '../lib/folderTree';
24
25
  import { type ChartView, type MountOptions } from '../charts/runtime';
25
26
  import { type ChartWindow, type ChartWindowOptions } from '../charts/chartWindow';
26
- import { type ColumnsInput } from './columns';
27
+ import { type ColumnsInput, type HistoryRangeView } from './columns';
27
28
  import { createDerivations } from './derive';
28
29
  import { chartLabel, type ChartKindInfo } from './kinds';
29
30
  import { createLiveController } from './liveController';
31
+ import type { LiveSegmentAxis } from '../lib/segments/liveAxis';
32
+ import { type ChartMark, type RuleStateInfo } from './alerts';
33
+ import type { RuleDoc } from '../rules/types';
34
+ import type { LinkedRuleLine } from '../lib/panelChartSettings';
30
35
  import { createStore } from './store';
31
- import { type ChartKind, type ChartModel, type EngineEvents, type EngineOptions, type EngineState, type KymoscopeLimits } from './types';
36
+ import { type ChartKind, type ChartModel, type EngineEvents, type EngineOptions, type EngineState, type KymoscopeLimits, type SegmentContext } from './types';
32
37
  export declare const DEFAULT_X = "ET";
33
38
  /** Default series / scatter colours (matches the app). */
34
39
  export declare const PALETTE: string[];
@@ -42,7 +47,10 @@ export interface InternalState {
42
47
  offsets: Record<string, number>;
43
48
  colors: Record<string, string>;
44
49
  liveWindowSeconds: number | null;
50
+ segmentContext: SegmentContext | null;
45
51
  live: LiveConnection[];
52
+ /** Video sources registered by the host (engine.video.register): recorded cameras. */
53
+ video: VideoSource[];
46
54
  mathChannels: MathChannel[];
47
55
  limits: KymoscopeLimits;
48
56
  palette: string[];
@@ -50,6 +58,10 @@ export interface InternalState {
50
58
  /** null until the licence check finishes. */
51
59
  licensed: boolean | null;
52
60
  history: EngineState['history'];
61
+ marks: ChartMark[];
62
+ alerts: RuleStateInfo[];
63
+ /** The rules in force (engine.alerts.rules()), here so a new set re-renders the lines linked to them. */
64
+ alertRules: RuleDoc[];
53
65
  }
54
66
  export type ImportSource = File | FolderEntry;
55
67
  export interface ImportResult {
@@ -90,6 +102,8 @@ export interface Exported {
90
102
  }
91
103
  export type ParsedFile = Awaited<ReturnType<typeof parseKymoscopeFile>>;
92
104
  export type ChartSettingsPatch = Partial<PanelChartSettings> & Record<string, unknown>;
105
+ /** Point every chart's column picks at columns the new data actually has (X: time when there is one). */
106
+ export declare function adoptColumns(ws: Worksheet, nums: string[]): Worksheet;
93
107
  export declare function createEngine(options?: EngineOptions): {
94
108
  getState: () => EngineState;
95
109
  subscribe(listener: () => void): () => void;
@@ -124,6 +138,13 @@ export declare function createEngine(options?: EngineOptions): {
124
138
  openRecording(input: RecordingInput): Promise<string>;
125
139
  /** New chunks of an open recording: the dataset keeps its id, selection, colour, offset and laps. */
126
140
  reopenRecording(id: string, input: RecordingInput): Promise<void>;
141
+ /**
142
+ * A time range of a Historian history as a dataset (pieces from readHistoryRange; X `ElapsedTime`, Unix seconds).
143
+ * Returns the id. Selected like any new dataset.
144
+ */
145
+ openHistoryRange(view: HistoryRangeView): string;
146
+ /** New pieces for a history dataset (pan/zoom, a later read): same id, selection, colour and offset. */
147
+ updateHistoryRange(id: string, view: HistoryRangeView): void;
127
148
  /** Add numeric columns from the host (selected immediately). */
128
149
  addColumns(input: ColumnsInput): string;
129
150
  /**
@@ -266,7 +287,12 @@ export declare function createEngine(options?: EngineOptions): {
266
287
  /** Linked X zoom of a worksheet (default: active); null = full range. */
267
288
  setXRange(range: [number, number] | null, worksheetId?: string): void;
268
289
  resetZoom(worksheetId?: string): void;
290
+ /** Shared chart cursor of a worksheet (default: active), in X data units; null = none. Fires 'cursorchange'. */
291
+ setCursorX(x: number | null, worksheetId?: string): void;
292
+ cursorX(worksheetId?: string): number | null;
269
293
  setLiveWindow(seconds: number | null): void;
294
+ /** The Segments panel's ids (history, definition, reference); saved with the workbook. */
295
+ setSegmentContext(ctx: SegmentContext | null): void;
270
296
  };
271
297
  mathChannels: {
272
298
  /** Create a channel (`name` and `formula` at least). Returns it. */
@@ -307,10 +333,22 @@ export declare function createEngine(options?: EngineOptions): {
307
333
  name: string;
308
334
  startedAt: number;
309
335
  } | null;
336
+ /** Live against a reference (Historian phase 5): the definition's rule, the axis column and deltas on a connection. */
337
+ setSegmentAxis(id: string, cfg: LiveSegmentAxis | null): void;
310
338
  /** Samples currently buffered for a connection (cheap; poll it for counters). */
311
339
  rowCount(id: string): number;
312
340
  disconnect(id: string): void;
313
341
  };
342
+ /** Camera video the Camera panel plays: live cameras appear on their own; register recorded ones. `videochange`
343
+ * fires when the list changes. */
344
+ video: {
345
+ /** Live cameras (kind 'live', one per camera connection) and the registered sources (recorded cameras). */
346
+ sources: () => VideoSource[];
347
+ /** Add a video source (e.g. a RecordedVideoSource of an open recording); replaces one with the same id and recordingId. */
348
+ register(source: VideoSource): void;
349
+ /** Remove the registered sources of camera `id` (of only that recording when `recordingId` is given). */
350
+ unregister(id: string, recordingId?: string): void;
351
+ };
314
352
  io: {
315
353
  /** True when there are charts that a workspace import would replace. */
316
354
  hasWork: () => boolean;
@@ -371,6 +409,24 @@ export declare function createEngine(options?: EngineOptions): {
371
409
  /** Like merged() without the math channels: no row cloning, stable until the data changes. */
372
410
  plotted(): Dataset | null;
373
411
  };
412
+ /** Alert rules (kyr/1) on the open datasets and live streams. The engine never stores rules: pass the whole list
413
+ * to setRules whenever it changes. Transitions arrive as `alert` events; episodes become `marks`. */
414
+ alerts: {
415
+ setRules: (docs: RuleDoc[]) => (import("..").RuleProblem & {
416
+ ruleId: string;
417
+ })[];
418
+ rules: () => RuleDoc[];
419
+ state: () => RuleStateInfo[];
420
+ evaluate: (datasetId: string, o: {
421
+ timeColumn: string;
422
+ ruleIds?: string[];
423
+ }) => Promise<import("..").AlertTransition[]>;
424
+ clearMarks: (targetId?: string) => void;
425
+ };
426
+ /** Read-only spans on the charts from alert episodes (never stored in chart settings). */
427
+ marks: {
428
+ list: () => ChartMark[];
429
+ };
374
430
  dispose(): void;
375
431
  };
376
432
  export type KymoscopeEngine = ReturnType<typeof createEngine>;
@@ -389,6 +445,8 @@ export interface EngineInternals {
389
445
  columnUnits: Record<string, string>;
390
446
  };
391
447
  live: ReturnType<typeof createLiveController>;
448
+ /** Per rule id: the threshold and state lines linked to it draw (resolveLinkedAlarms). */
449
+ alertLines: () => ReadonlyMap<string, LinkedRuleLine>;
392
450
  }
393
451
  export declare function getInternals(engine: KymoscopeEngine): EngineInternals;
394
452
  export { DATASET_PALETTE };
@@ -20,7 +20,7 @@ export declare function createDerivations(): {
20
20
  staticDataset: (selected: LoadedDataset[]) => Dataset | null;
21
21
  mergedDataset: (stat: Dataset | null, live: LiveConnection[]) => Dataset | null;
22
22
  mergedParts: (selected: LoadedDataset[], live: LiveConnection[]) => Dataset[];
23
- numericColumns: (merged: Dataset | null, selected: LoadedDataset[], channels: MathChannel[], parts?: Dataset[] | undefined) => string[];
23
+ numericColumns: (merged: Dataset | null, selected: LoadedDataset[], channels: MathChannel[], parts?: Dataset[] | undefined, live?: LiveConnection[] | undefined) => string[];
24
24
  allColumns: (merged: Dataset | null, selected: LoadedDataset[], channels: MathChannel[]) => string[];
25
25
  columnUnits: (merged: Dataset | null, channels: MathChannel[]) => Record<string, string>;
26
26
  };
@@ -1,4 +1,4 @@
1
- export type KymoscopeErrorCode = 'column_length_mismatch' | 'no_columns' | 'bad_workspace' | 'bad_file' | 'import_vetoed' | 'limit_reached' | 'not_found' | 'wrong_password' | 'password_required' | 'scope_key_required' | 'relay_unavailable' | 'disposed' | 'invalid_argument' | 'export_failed' | 'popup_blocked' | 'cancelled';
1
+ export type KymoscopeErrorCode = 'column_length_mismatch' | 'no_columns' | 'bad_workspace' | 'bad_file' | 'import_vetoed' | 'limit_reached' | 'not_found' | 'wrong_password' | 'password_required' | 'scope_key_required' | 'relay_unavailable' | 'disposed' | 'invalid_argument' | 'export_failed' | 'popup_blocked' | 'cancelled' | 'camera_codec_unsupported';
2
2
  /** Every failure the SDK reports. `detail` carries structured context (e.g. `{reason}` for limits). */
3
3
  export declare class KymoscopeError extends Error {
4
4
  readonly code: KymoscopeErrorCode;
@@ -1,5 +1,7 @@
1
+ import { LiveBuffer } from '../lib/live/buffer';
1
2
  import { type LiveRelayConfig } from '../lib/live/stream';
2
3
  import type { MathChannel } from '../lib/mathChannels/types';
4
+ import { type LiveSegmentAxis } from '../lib/segments/liveAxis';
3
5
  import type { LiveConnection, LiveStreamDescriptor, LiveStreamOptions, LocalLiveStream, StreamKeyProvider } from '../lib/live/types';
4
6
  export interface LiveController {
5
7
  connections(): LiveConnection[];
@@ -20,8 +22,16 @@ export interface LiveController {
20
22
  setRelay(relay: LiveRelayConfig | undefined): void;
21
23
  /** Math channels evaluated on every stream (as computed buffer columns). */
22
24
  setMathChannels(channels: MathChannel[]): void;
25
+ /** Run a segment definition live on a connection (plan S6), or stop it (null). Replays the ring quietly. */
26
+ setSegmentAxis(id: string, cfg: LiveSegmentAxis | null): void;
23
27
  dispose(): void;
24
28
  }
25
29
  export declare function createLiveController(initialRelay: LiveRelayConfig | undefined, onChange: (connections: LiveConnection[]) => void,
26
30
  /** The host's defaults for new streams (read when one opens). */
27
- defaults?: () => LiveStreamOptions): LiveController;
31
+ defaults?: () => LiveStreamOptions,
32
+ /** Alert rules: `rows` after every ingested block (from absolute index `from`, math channels already computed),
33
+ * `tick` once a second per stream with the arrival time (ms) of its last frame. */
34
+ hooks?: {
35
+ rows(id: string, buf: LiveBuffer, from: number): void;
36
+ tick(id: string, buf: LiveBuffer, lastFrameAt: number): void;
37
+ }): LiveController;
@@ -1,3 +1,4 @@
1
+ import type { VideoSource } from '../lib/live/videoTrack';
1
2
  import type { LiveStreamOptions } from '../lib/live/types';
2
3
  import type { DashboardPanelKind } from '../components/dashboardGridLayout';
3
4
  import type { PanelChartSettings } from '../lib/panelChartSettings';
@@ -7,15 +8,19 @@ import type { LiveRelayConfig } from '../lib/live/stream';
7
8
  import type { BeaconConfig } from '../lib/beacon';
8
9
  import type { DatasetParseConfig } from '../lib/parseDataset';
9
10
  import type { DatasetFileInfo } from '../lib/importers/types';
11
+ import type { AlertTransition } from '../rules/evaluator';
12
+ import type { ChartMark, RuleStateInfo } from './alerts';
10
13
  export type ChartKind = DashboardPanelKind;
11
14
  /** Why a limit blocked an action. */
12
- export type LimitReason = 'charts-per-worksheet' | 'channels-per-chart' | 'worksheets' | 'workspaces-io' | 'math-channels';
15
+ export type LimitReason = 'charts-per-worksheet' | 'channels-per-chart' | 'worksheets' | 'workspaces-io' | 'math-channels' | 'alert-rules';
13
16
  /** Quantity limits; `null` = unlimited. */
14
17
  export interface KymoscopeLimits {
15
18
  maxChartsPerWorksheet: number | null;
16
19
  maxChannelsPerChart: number | null;
17
20
  maxWorksheets: number | null;
18
21
  maxMathChannels: number | null;
22
+ /** Alert rules evaluated (engine.alerts.setRules keeps the first ones). */
23
+ maxAlertRules: number | null;
19
24
  canImportExportWorkspaces: boolean;
20
25
  }
21
26
  export declare const UNLIMITED: KymoscopeLimits;
@@ -127,7 +132,14 @@ export interface LiveConnectionInfo {
127
132
  ownerName: string;
128
133
  visibility: 'private' | 'public' | 'org' | 'project';
129
134
  orgName: string | null;
130
- status: 'connecting' | 'live' | 'stale' | 'error';
135
+ /** A camera carries video (a camera panel shows it); a data stream, samples. */
136
+ kind: 'data' | 'camera';
137
+ /** A camera's data stream; null for data streams. */
138
+ parentId: string | null;
139
+ /** A camera's low layer: its high camera's id (not a camera of its own: lists and pickers skip it); null otherwise. */
140
+ lowOf: string | null;
141
+ /** 'idle': a camera no panel shows (connected, not downloading). */
142
+ status: 'idle' | 'connecting' | 'live' | 'stale' | 'error';
131
143
  error: string | null;
132
144
  rowCount: number;
133
145
  selected: boolean;
@@ -137,6 +149,14 @@ export interface LiveConnectionInfo {
137
149
  customColor: boolean;
138
150
  /** When a recording is running: its start time (ms since epoch), else null. */
139
151
  recordingSince: number | null;
152
+ /** Its columns as of the last structural change (the time column first). */
153
+ channels: string[];
154
+ }
155
+ /** What the Segments panel shows (Historian phase 5): saved in workbooks by id (spec E4), never data. */
156
+ export interface SegmentContext {
157
+ historyId: string;
158
+ definitionId: string | null;
159
+ referenceId: string | null;
140
160
  }
141
161
  export interface EngineState {
142
162
  datasets: DatasetInfo[];
@@ -148,6 +168,8 @@ export interface EngineState {
148
168
  live: LiveConnectionInfo[];
149
169
  /** Scrolling window for live charts on a time X axis (seconds; null = all). */
150
170
  liveWindowSeconds: number | null;
171
+ /** The Segments panel's history, definition and reference, or null. */
172
+ segmentContext: SegmentContext | null;
151
173
  limits: KymoscopeLimits;
152
174
  /** Colors given to plotted datasets in order (EngineOptions.palette). */
153
175
  palette: string[];
@@ -173,16 +195,33 @@ export interface EngineState {
173
195
  canUndo: boolean;
174
196
  canRedo: boolean;
175
197
  };
198
+ /** Read-only spans from alert rule episodes (engine.alerts), drawn on time charts. */
199
+ marks: ChartMark[];
200
+ /** Per rule and open target: waiting (with the missing channel names), idle, pending, active, clearing… */
201
+ alerts: RuleStateInfo[];
176
202
  }
177
203
  export interface EngineEvents extends Record<string, unknown> {
178
204
  change: EngineState;
179
205
  limit: {
180
206
  reason: LimitReason;
181
207
  };
208
+ /** An alert rule raised, cleared, or lost its channel on a target. */
209
+ alert: AlertTransition;
210
+ /** The shared chart cursor of a worksheet moved (view.setCursorX); x in data units, null = off the charts. */
211
+ cursorchange: {
212
+ worksheetId: string;
213
+ x: number | null;
214
+ };
215
+ /** engine.video.sources() changed: a live camera came or went, or a source was registered or unregistered. */
216
+ videochange: {
217
+ sources: VideoSource[];
218
+ };
182
219
  error: {
183
220
  error: Error;
184
221
  context: string;
185
222
  };
223
+ /** The engine was disposed (once; every call throws KymoscopeError 'disposed' from now on). */
224
+ dispose: undefined;
186
225
  import: {
187
226
  file: string;
188
227
  phase: 'start' | 'progress' | 'done' | 'error';
@@ -14,6 +14,7 @@ export { KymoscopeError } from './engine/errors';
14
14
  export type { KymoscopeErrorCode } from './engine/errors';
15
15
  export { UNLIMITED } from './engine/types';
16
16
  export type { EngineOptions, EngineState, EngineEvents, KymoscopeLimits, LimitReason, MathChannelStoreAdapter, DatasetInfo, ColumnInfo, LapInfo, ChartKind, ChartModel, WorksheetState, LiveConnectionInfo, } from './engine/types';
17
+ export type { ChartMark, RuleStateInfo } from './engine/alerts';
17
18
  export { CHART_KINDS, chartLabel } from './engine/kinds';
18
19
  export type { ChartKindInfo } from './engine/kinds';
19
20
  export type { ColumnsInput } from './engine/columns';
@@ -24,7 +25,7 @@ export type { ChartWindow, ChartWindowOptions } from './charts/chartWindow';
24
25
  export type { ChartStatus, ContextMenuEvent } from './charts/ChartBody';
25
26
  export * from './lib/panelChartSettings';
26
27
  export { chartChannelCount } from './lib/chartChannels';
27
- export { ANNOTATION_KINDS, DEFAULT_ANNOTATION_WIDTH, type AnnotationKind, type AnnotationTool, type ChartAnnotation } from './lib/annotations';
28
+ export { ANNOTATION_KINDS, DEFAULT_ANNOTATION_WIDTH, coerceAnnotation, markToAnnotation, SEVERITY_HEX, type AnnotationKind, type AnnotationTool, type ChartAnnotation } from './lib/annotations';
28
29
  export { ANNOTATION_CONTROLS_ATTR } from './charts/AnnotationLayer';
29
30
  export { colorAt, colorsCss, presetStops, type GaugeColorMode, type GaugeColorStop, type GaugePreset } from './lib/gaugeColors';
30
31
  export type { PlotSeries, AlarmLine, AlarmStyle } from './types/plotSeries';
@@ -60,11 +61,33 @@ export type { LiveRelayConfig } from './lib/live/stream';
60
61
  export type { LiveStreamDescriptor, LiveStreamOptions, LocalLiveStream, StreamKeyProvider } from './lib/live/types';
61
62
  export type { LiveColumnsBlock, LiveMessage } from './lib/live/buffer';
62
63
  export { LIVE_TIME_COLUMN } from './lib/live/buffer';
63
- export { openMessage, sealMessage } from './lib/live/wire';
64
+ export { openMessage, sealMessage, KIND_VIDEO } from './lib/live/wire';
65
+ export { encodeVideo, decodeVideo, fragmentVideo, VIDEO_MAX_FRAGMENT, type VideoMessage } from './lib/live/videoWire';
66
+ export type { VideoSource, EncodedUnit, VideoTrackInfo } from './lib/live/videoTrack';
67
+ export { RecordedVideoSource, type RecordedVideoSourceOptions } from './lib/recordings/videoSource';
68
+ export { LivePublisher, type PublisherDeps, type PublisherOptions, type PublisherState, type PublisherStatus } from './lib/live/publisher';
69
+ export { CameraSender, CAMERA_PRESETS, LOW_PRESET, type CameraPreset, type CameraStats, type CameraLayerStats, type CameraSenderDeps } from './lib/live/cameraSender';
64
70
  export type { RecordingKey } from './lib/recordings/keys';
65
71
  export { recordingKeyAad, recordingKeySealAad } from './lib/recordings/keys';
66
72
  export { parseKrc, parseKrcHeader, KRC_HEADER_BYTES } from './lib/recordings/krc';
67
73
  export type { RecordingInput } from './engine/createEngine';
68
74
  export { exportRecording, exportRecordingTo, EXPORT_TYPES, type ExportPart, type RecordingSource } from './lib/recordings/export';
69
75
  export type { EncryptedEnvelope, DeviceWrap, SealedKey, PasswordSealed } from './lib/live/crypto';
70
- export { generateApiToken, splitApiKey, generateDeviceKeyPair, unwrapStreamKey, unwrapForDevice, wrapForDevice, projectWrapAad, streamKeyAad, bindKeyAad, randomKey, importStreamKey, sealUnderProjectKey, openUnderProjectKey, senderBindKey, senderBinding, verifySenderBinding, deviceFingerprint, hashToken, generateSalt, deriveKey, deriveRawKeyBase64, makeKeyCheck, verifyKeyCheck, WORKBOOK_EXPORT_AAD, workbookBodyAad, workbookKeyAad, recoveryKeyAad, recoveryAttestationAad, sealWithKey, openWithKey, sealWithPassword, openWithPassword, recoveryCode, parseRecoveryCode, generateRecoveryKeyPair, recoveryPrivateKey, } from './lib/live/crypto';
76
+ export { generateApiToken, splitApiKey, generateDeviceKeyPair, unwrapStreamKey, unwrapForDevice, wrapForDevice, projectWrapAad, streamKeyAad, bindKeyAad, randomKey, importStreamKey, sealUnderProjectKey, openUnderProjectKey, senderBindKey, senderBinding, verifySenderBinding, deviceFingerprint, hashToken, generateSalt, deriveKey, deriveRawKeyBase64, makeKeyCheck, verifyKeyCheck, WORKBOOK_EXPORT_AAD, workbookBodyAad, workbookKeyAad, historyKeyAad, projectConfigKeyAad, alertRuleAad, recoveryKeyAad, recoveryAttestationAad, sealWithKey, openWithKey, sealWithPassword, openWithPassword, recoveryCode, parseRecoveryCode, generateRecoveryKeyPair, recoveryPrivateKey, } from './lib/live/crypto';
77
+ export * from './rules/types';
78
+ export { validateRule, type RuleProblem } from './rules/validate';
79
+ export { thresholdOf } from './rules/threshold';
80
+ export { createRuleEvaluator, type RuleEvaluator, type RuleTarget, type AlertTransition, type RuleEvaluatorState, type LiveBlock } from './rules/evaluator';
81
+ export { footerLength, openFooter, openSealed, blockAad, type KhdFooter, type KhdBlock, type KhdChannel, type KhdFileId, type HistoryKind, type HistoryLevel } from './lib/historian/khd1';
82
+ export { warmHistoryDecoder } from './lib/historian/parquet';
83
+ export { blockCache, type BlockCache, type CacheDir } from './lib/historian/blockCache';
84
+ export { HistoryDataSource, splicePieces, clipPiece, slicePiece, type HistoryPiece } from './lib/data/historyDataSource';
85
+ export type { HistoryRangeView } from './engine/columns';
86
+ export { isClockAxis, formatClockTick, formatClockValue, setClockTimeZone, getClockTimeZone, subscribeClockTimeZone } from './lib/yAxisTicks';
87
+ export { readHistoryRange, readFooter, chooseLevel, pickSegments, coalesce, pool, RANGE_PARALLEL, HISTORY_PIXELS, type HistorySegmentRef, type HistoryReadOptions, type HistoryRange } from './lib/historian/reader';
88
+ export { exportHistoryTo, wideDay, f32text, isoMicros, MAX_WIDE_CELLS, MAX_PARQUET_VALUES, type HistoryExportFormat } from './lib/historian/export';
89
+ export { RuleRunner, resetStep, newResetState, compareOp, kpiKey, ruleChannels, localDay, zonedInstant, shiftDay, tzOffsetS, KPI_STATS, type SegmentRule, type CompareOp, type EdgeOn, type KpiStat, type KpiSpec, type Kpis, type DetectedSegment, type ResetState } from './lib/segments/rules';
90
+ export type { SegmentContext } from './engine/types';
91
+ export { referenceBands } from './charts/beacon';
92
+ export { alignSegment, AxisState, axisColumn, localSeconds, resample, gridAt, buildReference, bandEdges, compareToReference, encodeBand, decodeBand, MAX_GRID, MAX_BAND_CELLS, type AlignAxis, type AlignedSegment, type ReferenceCurve, type ReferenceChannel, type Comparison } from './lib/segments/align';
93
+ export { liveSegmentTracker, DELTA_SUFFIX, type LiveSegmentAxis, type LiveSegmentEvent, type LiveReference } from './lib/segments/liveAxis';
@@ -35,3 +35,23 @@ export declare const DEFAULT_ANNOTATION_WIDTH: Record<AnnotationKind, number>;
35
35
  /** One annotation from untrusted input (a file, an SDK call), or null if it isn't one. */
36
36
  export declare function coerceAnnotation(v: unknown): ChartAnnotation | null;
37
37
  export declare function coerceAnnotations(v: unknown): ChartAnnotation[] | null;
38
+ /** Severity colours of alert marks: the palette's own muted text, amber accent and danger (hex for annotations). */
39
+ export declare const SEVERITY_HEX: {
40
+ readonly info: "#8a93a6";
41
+ readonly warning: "#f5a524";
42
+ readonly critical: "#e5484d";
43
+ };
44
+ /** The same, following the page's theme on screen. */
45
+ export declare const SEVERITY_COLOR: {
46
+ readonly info: "var(--text-muted, #8a93a6)";
47
+ readonly warning: "var(--accent, #f5a524)";
48
+ readonly critical: "var(--danger, #e5484d)";
49
+ };
50
+ /** An alert mark as a box annotation pinned to X ("Keep as annotation"); an open mark ends at `rightEdgeX`. */
51
+ export declare function markToAnnotation(m: {
52
+ id: string;
53
+ ruleName: string;
54
+ severity: keyof typeof SEVERITY_HEX;
55
+ t0: number;
56
+ t1: number | null;
57
+ }, rightEdgeX: number): ChartAnnotation;
@@ -22,7 +22,8 @@ export interface LapRange {
22
22
  /**
23
23
  * Split rows wherever `channel` resets. A reset is a drop larger than half of
24
24
  * the current lap's span so far, so small sensor jitter backwards never starts
25
- * a new lap. Rows whose channel is not numeric stay inside the current lap.
25
+ * a new lap (the edge rule `reset` of segments/rules.ts). Rows whose channel is
26
+ * not numeric stay inside the current lap.
26
27
  */
27
28
  export declare function splitLaps(rows: DatasetRow[], channel: string): LapRange[];
28
29
  /**
@@ -19,6 +19,7 @@ export declare class ColumnarDataSource implements DataSource {
19
19
  /** {@link getSeriesMulti} without the Promise: decimating the columns is pure computation. */
20
20
  getSeriesMultiSync(columns: string[], viewport: Viewport, maxPoints: number): MultiSeriesResult;
21
21
  getColumnsSync(columns: string[], viewport: Viewport, limit: number): PointColumns | null;
22
+ readColumns(columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
22
23
  xBounds(column: string): Promise<{
23
24
  x0: number;
24
25
  x1: number;
@@ -61,6 +61,9 @@ export interface DataSource {
61
61
  * when there are more than `limit` of them or X isn't ascending.
62
62
  */
63
63
  getColumnsSync?(columns: string[], viewport: Viewport, limit: number): PointColumns | null;
64
+ /** The rows [offset, offset + limit) of `columns` in file order, as numbers (NaN for empty or text cells).
65
+ * Unknown columns are left out. For whole-column passes (alert rules on loaded data); read in chunks. */
66
+ readColumns(columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
64
67
  xBoundsSync?(column: string): {
65
68
  x0: number;
66
69
  x1: number;
@@ -33,6 +33,7 @@ export declare class DuckDbDataSource implements DataSource {
33
33
  * a narrow window are denser, so the same `maxRows` budget buys finer detail.
34
34
  */
35
35
  sampleRows(maxRows: number, range?: XRange): Promise<DatasetRow[]>;
36
+ readColumns(columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
36
37
  /** Full-range x bounds for a column, for an initial viewport. */
37
38
  xBounds(xKey: string): Promise<{
38
39
  x0: number;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Historian ranges as a DataSource (plan P5): a list of pieces, each one block's times with Float32 values per channel
3
+ * (raw: lo and hi are the same arrays; a rollup: the bucket's min and max at its middle). The time column is
4
+ * `ElapsedTime` in Unix seconds, the same X as a collector's live stream. Decimation reads each piece on its own times,
5
+ * so tag groups never share a padded table. Everything is pure computation (sync), like the in-memory sources.
6
+ */
7
+ import type { ColumnSchema, DataSource, DecimatedSeries, MultiSeriesResult, SeriesQuery, Viewport } from './dataSource';
8
+ import type { DatasetRow } from '../parseDataset';
9
+ import type { HistoryLevel } from '../historian/khd1';
10
+ export interface HistoryPiece {
11
+ /** Unix seconds, ascending. */
12
+ t: Float64Array;
13
+ /** Per channel: the value (raw) or the bucket minimum (rollup). NaN: no value at that time. */
14
+ lo: Map<string, Float32Array>;
15
+ /** Per channel: the bucket maximum (rollup); the very same Map as `lo` for raw data. */
16
+ hi: Map<string, Float32Array>;
17
+ level: HistoryLevel;
18
+ }
19
+ /** First index with t[i] >= x, or with t[i] > x when `after`. */
20
+ export declare function bound(t: Float64Array, x: number, after?: boolean): number;
21
+ /** Rows [i, j) of a piece (views, nothing copied); null when empty. */
22
+ export declare function slicePiece(p: HistoryPiece, i: number, j: number): HistoryPiece | null;
23
+ /** The rows of `p` with t in [t0, t1]. */
24
+ export declare const clipPiece: (p: HistoryPiece, t0: number, t1: number) => HistoryPiece | null;
25
+ /** The overview outside [a, b] with the detail inside: the pan/zoom view (plan P6). */
26
+ export declare function splicePieces(overview: HistoryPiece[], detail: HistoryPiece[], a: number, b: number): HistoryPiece[];
27
+ export declare class HistoryDataSource implements DataSource {
28
+ #private;
29
+ readonly pieces: readonly HistoryPiece[];
30
+ constructor(pieces: HistoryPiece[], units?: Record<string, string>);
31
+ schemaSync(): ColumnSchema[];
32
+ getSchema(): Promise<ColumnSchema[]>;
33
+ /** Rows are the pieces' rows in time order (a rollup: its minimum); NaN where a piece lacks the channel. */
34
+ readColumns(columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
35
+ getSeriesMultiSync(columns: string[], viewport: Viewport, maxPoints: number): MultiSeriesResult;
36
+ getSeriesMulti(columns: string[], viewport: Viewport, maxPoints: number): Promise<MultiSeriesResult>;
37
+ getSeries(q: SeriesQuery): Promise<DecimatedSeries>;
38
+ xBoundsSync(column: string): {
39
+ x0: number;
40
+ x1: number;
41
+ };
42
+ xBounds(column: string): Promise<{
43
+ x0: number;
44
+ x1: number;
45
+ }>;
46
+ /** At most `maxRows` rows in time order for the row-based charts and math channels: one stride over all pieces' rows
47
+ * end to end (pieces smaller than the stride may be skipped); a row holds its piece's channels only (a rollup: the
48
+ * bucket minimum). ponytail: no cross-group alignment in the sample. */
49
+ sampleRowsSync(maxRows: number): DatasetRow[];
50
+ dispose(): Promise<void>;
51
+ }
@@ -12,6 +12,7 @@ export declare class InMemoryDataSource implements DataSource {
12
12
  getSeriesMultiSync(columns: string[], viewport: Viewport, maxPoints: number): MultiSeriesResult;
13
13
  /** {@link DataSource.getColumnsSync}: a table's columns are the samples (other rows: null, decimate). */
14
14
  getColumnsSync(columns: string[], viewport: Viewport, limit: number): PointColumns | null;
15
+ readColumns(columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
15
16
  /** Exact min/max like the DuckDB source ({0, 0} when the column has no numbers), not `columnBounds`' padded axis range. */
16
17
  xBounds(column: string): Promise<{
17
18
  x0: number;
@@ -16,6 +16,8 @@ export declare class LiveDataSource implements DataSource {
16
16
  * a host's sample-count badge show the climbing count by polling itself, instead of the
17
17
  * count riding on the ~1 Hz `connections` state whose update re-renders the App. */
18
18
  size(): number;
19
+ /** The stream's column names now (the time column first). */
20
+ get columnNames(): string[];
19
21
  /** Changes whenever the buffered data does (see LiveBuffer.version). */
20
22
  version(): number;
21
23
  /**
@@ -24,6 +26,8 @@ export declare class LiveDataSource implements DataSource {
24
26
  * LiveBuffer.mirrorColumn). A new map per call: the columns as they are now.
25
27
  */
26
28
  columns(): ReadonlyMap<string, Float64Array>;
29
+ /** Whether `column` has had a finite value (a column the snapshot rows may thin out of view). */
30
+ hasValues(column: string): boolean;
27
31
  /** A fresh row snapshot of the newest samples (at most `maxRows`, evenly thinned). */
28
32
  snapshot(maxRows: number): Dataset;
29
33
  getSchema(): Promise<ColumnSchema[]>;
@@ -54,11 +58,25 @@ export declare class LiveDataSource implements DataSource {
54
58
  /** Time of the newest sample (NaN if empty). */
55
59
  latestTimeSync(): number;
56
60
  /** Value of `column` at time `t` (see LiveBuffer.xAtTime). */
57
- xAtTimeSync(column: string, t: number): number;
61
+ xAtTimeSync(column: string, t: number, hold?: boolean): number;
58
62
  /** The newest sample's value of `column` (NaN if none): the live position on X. */
59
63
  latestSync(column: string): number;
60
- /** Newest received value of each column it has (finite values only). */
64
+ /** Newest received value of each column it has (finite values only): the newest sample's, else the column's
65
+ * last known value (a channel that has no value at the newest time, or is quiet). */
61
66
  latestValuesSync(columns: string[]): Map<string, number>;
67
+ /**
68
+ * Y range of `column` for a scale: its samples in the ring and its last known value (a quiet channel has only
69
+ * that); a constant gets ±max(0.5, 5%) so it sits mid-band. Null when it has neither.
70
+ */
71
+ valueBoundsSync(column: string): {
72
+ min: number;
73
+ max: number;
74
+ } | null;
75
+ /** The newest finite value of `column` and its time, past the ring (see LiveBuffer.lastKnown). */
76
+ lastKnownSync(column: string): {
77
+ t: number;
78
+ v: number;
79
+ } | undefined;
62
80
  /** Follow cycles on `xKey` (beacon): see LiveBuffer.trackCycles. */
63
81
  trackCycles(xKey: string): void;
64
82
  /** X span of every sample seen on a tracked key, or null. */
@@ -73,5 +91,6 @@ export declare class LiveDataSource implements DataSource {
73
91
  x0: number;
74
92
  x1: number;
75
93
  }, maxPoints: number): PointColumns;
94
+ readColumns(columns: string[], offset: number, limit: number): Promise<Record<string, Float64Array>>;
76
95
  dispose(): Promise<void>;
77
96
  }