@kymoscope/sdk 0.3.0 → 0.5.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 (78) 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-Y-PAIexV.js +5 -0
  4. package/dist/keys-DMRJGG6U.js +10 -0
  5. package/dist/kymoscope-embed.js +92 -90
  6. package/dist/kymoscope-sdk.js +87 -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 +3 -5
  19. package/dist/types/components/GaugeChart.d.ts +2 -0
  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 +111 -3
  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 +69 -4
  30. package/dist/types/index.d.ts +29 -4
  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/cycles/cut.d.ts +48 -0
  34. package/dist/types/lib/cycles/evaluator.d.ts +127 -0
  35. package/dist/types/lib/data/columnarDataSource.d.ts +1 -0
  36. package/dist/types/lib/data/dataSource.d.ts +3 -0
  37. package/dist/types/lib/data/duckDbDataSource.d.ts +1 -0
  38. package/dist/types/lib/data/historyDataSource.d.ts +51 -0
  39. package/dist/types/lib/data/inMemoryDataSource.d.ts +1 -0
  40. package/dist/types/lib/data/liveDataSource.d.ts +20 -1
  41. package/dist/types/lib/data/pointColumns.d.ts +11 -0
  42. package/dist/types/lib/duckdb/duckdbInstance.d.ts +5 -0
  43. package/dist/types/lib/historian/blockCache.d.ts +32 -0
  44. package/dist/types/lib/historian/export.d.ts +31 -0
  45. package/dist/types/lib/historian/khd1.d.ts +41 -0
  46. package/dist/types/lib/historian/parquet.d.ts +21 -0
  47. package/dist/types/lib/historian/reader.d.ts +73 -0
  48. package/dist/types/lib/live/blockWire.d.ts +16 -0
  49. package/dist/types/lib/live/buffer.d.ts +34 -0
  50. package/dist/types/lib/live/cameraSender.d.ts +70 -0
  51. package/dist/types/lib/live/clockMap.d.ts +20 -0
  52. package/dist/types/lib/live/crypto.d.ts +10 -0
  53. package/dist/types/lib/live/publisher.d.ts +168 -0
  54. package/dist/types/lib/live/types.d.ts +21 -3
  55. package/dist/types/lib/live/videoTrack.d.ts +96 -0
  56. package/dist/types/lib/live/videoWire.d.ts +35 -0
  57. package/dist/types/lib/live/wire.d.ts +4 -0
  58. package/dist/types/lib/panelChartSettings.d.ts +40 -1
  59. package/dist/types/lib/recordings/export.d.ts +1 -0
  60. package/dist/types/lib/recordings/videoSource.d.ts +56 -0
  61. package/dist/types/lib/render/alertGlyph.d.ts +7 -0
  62. package/dist/types/lib/render/glSeries.d.ts +1 -1
  63. package/dist/types/lib/segments/align.d.ts +85 -0
  64. package/dist/types/lib/segments/liveAxis.d.ts +65 -0
  65. package/dist/types/lib/segments/rules.d.ts +91 -0
  66. package/dist/types/lib/workspaces/io.d.ts +12 -0
  67. package/dist/types/lib/yAxisTicks.d.ts +11 -0
  68. package/dist/types/rules/compile.d.ts +47 -0
  69. package/dist/types/rules/evaluator.d.ts +51 -0
  70. package/dist/types/rules/index.d.ts +4 -0
  71. package/dist/types/rules/threshold.d.ts +3 -0
  72. package/dist/types/rules/types.d.ts +89 -0
  73. package/dist/types/rules/validate.d.ts +6 -0
  74. package/dist/types/types/loadedDataset.d.ts +20 -0
  75. package/dist/types/types/plotSeries.d.ts +5 -0
  76. package/package.json +7 -2
  77. package/dist/importers-DHeUdZcP.js +0 -5
  78. package/dist/krc-BIfw0rX1.js +0 -9
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Data-stream time → wall time (Unix seconds), for syncing a camera with a data stream.
3
+ * Senders that stamp samples with Unix seconds (t > 1e9) need no map: "exact". Others
4
+ * get an estimate: the minimum over a sliding window of (arrival − last sample time of
5
+ * the message), i.e. the clock offset plus the smallest network delay seen: "approx".
6
+ */
7
+ export type ClockQuality = 'exact' | 'approx' | 'none';
8
+ export declare class ClockMap {
9
+ private samples;
10
+ private exact;
11
+ private windowS;
12
+ constructor(windowS?: number);
13
+ /** A data message arrived at wall time `arrival` (s) whose last sample has stream time `t`. */
14
+ observe(t: number, arrival: number): void;
15
+ quality(): ClockQuality;
16
+ /** Stream time → wall seconds; `offsetMs` is the user's manual correction. */
17
+ toWall(t: number, offsetMs?: number): number | null;
18
+ /** Wall seconds → stream time (to draw the "frame shown" marker on a chart). */
19
+ toStream(wall: number, offsetMs?: number): number | null;
20
+ }
@@ -15,6 +15,9 @@ export interface EncryptedEnvelope {
15
15
  iv: string;
16
16
  ciphertext: string;
17
17
  }
18
+ /** An open stream's password (visibility 'open', spec 2026-10-31-stream-access-model): anyone watches with nothing
19
+ * typed, so its key derives from this published text. Not end-to-end encrypted, by design. Same in Python and plugins. */
20
+ export declare const OPEN_STREAM_PASSWORD = "kymoscope-open-stream-v1";
18
21
  /** Random 16-byte KDF salt, base64-encoded. Stored server-side; not secret. */
19
22
  export declare function generateSalt(): string;
20
23
  /** Derive an AES-256-GCM key from a passphrase + base64 salt via PBKDF2. */
@@ -117,6 +120,13 @@ export declare function deviceFingerprint(publicKey: string): Promise<string>;
117
120
  export declare const WORKBOOK_EXPORT_AAD = "kymoscope-workbook-export-v2";
118
121
  export declare const workbookBodyAad: (workbookId: string, version: number) => string;
119
122
  export declare const workbookKeyAad: (workbookId: string, projectId: string, epoch: number) => string;
123
+ /** A collector's day-history key sealed under the project key (resource_keys 'history_key'). Python twin:
124
+ * kymoscope.crypto.history_key_aad. */
125
+ export declare const historyKeyAad: (keyId: string, projectId: string, epoch: number) => string;
126
+ /** A project's config key (alert rules) sealed under the project key: resource_keys 'project_config', id = the project. */
127
+ export declare const projectConfigKeyAad: (projectId: string, epoch: number) => string;
128
+ /** An alert rule's kyr/1 body sealed under its project's config key: bound to the rule, its version and project. */
129
+ export declare const alertRuleAad: (ruleId: string, version: number, projectId: string) => string;
120
130
  export declare const recoveryKeyAad: (orgId: string, deviceId: string) => string;
121
131
  /** The recovery attestation: SHA-256 of the recovery public key sealed under a project key (plan Decision 30). */
122
132
  export declare const recoveryAttestationAad: (orgId: string, projectId: string, epoch: number) => string;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * A browser sender of Live Streams: the TypeScript port of the Python sender's protocol
3
+ * (packages/python/src/kymoscope/stream.py LiveStream + _RelayTransport, client.py). Generic over the
4
+ * message kind: the caller hands plaintext to send(kind, plaintext) and the publisher seals and publishes it.
5
+ *
6
+ * stream-token (`x-api-key`: the key's auth part) gives a `pt.` token; register and every heartbeat
7
+ * (stream-publisher) give the `pp.` ticket the relay's publish socket takes. A password stream's key
8
+ * derives from the API key's secret part, which never leaves this side, and goes to the owner's browsers
9
+ * sealed to each (device wraps). A key of an end-to-end encrypted project makes this sender a device of
10
+ * the project: nothing is published until a member's browser approves it, then a random stream key goes
11
+ * up sealed under the project key and messages carry the key id (epoch & 0xff).
12
+ *
13
+ * Persisted key material stays as non-extractable CryptoKeys: an e2ee stream key is kept with its
14
+ * project wrap ({iv, ct}, not secret), which a restart within the epoch uploads again instead of
15
+ * re-sealing raw bytes it no longer has.
16
+ */
17
+ import { type SealedKey } from './crypto';
18
+ /** What a sender keeps across restarts (per API key). Device and stream keys only count for `apiKeyId`. */
19
+ export interface PublisherState {
20
+ apiKeyId: string | null;
21
+ streamId: string;
22
+ salt: string;
23
+ /** An e2ee project's sender device (non-extractable private key). */
24
+ device: {
25
+ privateKey: CryptoKey;
26
+ publicKey: string;
27
+ } | null;
28
+ /** An e2ee stream key, reused only within its epoch, with its project wrap. */
29
+ streamKey: {
30
+ epoch: number;
31
+ key: CryptoKey;
32
+ sealed: SealedKey;
33
+ } | null;
34
+ }
35
+ export interface PublisherDeps {
36
+ fetch: typeof fetch;
37
+ WebSocket: typeof WebSocket;
38
+ /** ms epoch */
39
+ now: () => number;
40
+ store: {
41
+ load(): Promise<PublisherState | null>;
42
+ save(s: PublisherState): Promise<void>;
43
+ };
44
+ }
45
+ export interface PublisherOptions {
46
+ supabaseUrl: string;
47
+ apiKey: string;
48
+ name: string;
49
+ kind: 'data' | 'camera';
50
+ /** Where the stream goes is the API key's: a project's key publishes into that project, a personal key a personal stream. */
51
+ visibility?: 'private' | 'public' | 'open';
52
+ /** The stream password viewers type (spec 2026-10-31-stream-access-model); default: the API key's secret. 'open'
53
+ * streams take none: anyone watches them (a published password; not end-to-end encrypted). */
54
+ password?: string;
55
+ /** The caller's own data stream a camera belongs to (required for kind 'camera'); its placement comes from it. */
56
+ parentStreamId?: string;
57
+ /** A camera's low layer (360p, for small panels and camera walls): `layer: 'low'` with `lowOf`, its high camera's
58
+ * stream id (same parent). Both or neither. */
59
+ layer?: 'low';
60
+ lowOf?: string;
61
+ }
62
+ export type PublisherStatus = {
63
+ phase: 'registering';
64
+ } | {
65
+ phase: 'waiting_approval';
66
+ } | {
67
+ phase: 'live';
68
+ viewers: number;
69
+ } | {
70
+ phase: 'reconnecting';
71
+ } | {
72
+ phase: 'refused';
73
+ code: string;
74
+ message: string;
75
+ };
76
+ export declare class LivePublisher {
77
+ private readonly opts;
78
+ private readonly deps;
79
+ private readonly auth;
80
+ private readonly secret;
81
+ /** What the stream key derives from: the stream password, the open streams' published one, or the API key's secret. */
82
+ private readonly password;
83
+ private token;
84
+ private ticket;
85
+ /** The current ticket says rec (the reply's `recording`); `authRec`: the one the open socket authenticated with. */
86
+ private ticketRec;
87
+ private authRec;
88
+ private relayUrl;
89
+ private apiKeyId;
90
+ private projectId;
91
+ private e2ee;
92
+ private sid;
93
+ private salt;
94
+ private key;
95
+ /** A password stream's raw key, for device wraps (derivable from the secret anyway). */
96
+ private raw;
97
+ private epoch;
98
+ private haveKey;
99
+ private sealed;
100
+ private device;
101
+ private senderDeviceId;
102
+ private deviceRetried;
103
+ private maxBytesPerSec;
104
+ private maxMessageBytes;
105
+ private ws;
106
+ private open;
107
+ private everOpen;
108
+ private connectedAt;
109
+ private authAt;
110
+ private delay;
111
+ private lastToken;
112
+ private viewerCount;
113
+ private busy;
114
+ private stopped;
115
+ private refusal;
116
+ private heartbeatTimer;
117
+ private pingTimer;
118
+ private retryTimer;
119
+ private readonly statusFns;
120
+ private readonly viewerFns;
121
+ private lastStatus;
122
+ constructor(opts: PublisherOptions, deps: PublisherDeps);
123
+ /** The registered stream id (null until start() registers). */
124
+ get streamId(): string | null;
125
+ /** The relay's viewer count (0 while disconnected). */
126
+ get viewers(): number;
127
+ /** Bytes/second the account negotiated (0: not limited) and the relay's message size limit. */
128
+ get limits(): {
129
+ maxBytesPerSec: number;
130
+ maxMessageBytes: number;
131
+ };
132
+ onStatus(fn: (s: PublisherStatus) => void): () => void;
133
+ onViewers(fn: (n: number) => void): () => void;
134
+ /** Mint, register (reusing a persisted stream id and salt), open the socket, heartbeat. Rejects when
135
+ * registration fails; a 4xx (a refusal) also sets status `refused`. */
136
+ start(): Promise<void>;
137
+ /** Seal with the current stream key (the key id of its epoch when e2ee) and send; false if not sent
138
+ * (no key yet, socket down, stopped). */
139
+ send(kind: number, plaintext: Uint8Array): Promise<boolean>;
140
+ /** Close the socket and stop the timers. The stream stays registered (never deleted here). */
141
+ stop(): void;
142
+ private post;
143
+ private mint;
144
+ private call;
145
+ private register;
146
+ private senderDevice;
147
+ /** Token refresh, then the heartbeat (each new ticket also refreshes the socket). 401/403 stop the
148
+ * publisher (revoked key, plan, stale_epoch); anything else is retried on the next interval. */
149
+ private housekeeping;
150
+ /** Seal the stream key to the owner's browsers that lack it (password streams), at most 50 per call. */
151
+ private wrapFor;
152
+ /** The project key arrived: seal the stream key under it; a new epoch (rotation) gets a new stream key.
153
+ * A key this device can't open re-registers once with a new device key. */
154
+ private applyProject;
155
+ private reregister;
156
+ private save;
157
+ private connect;
158
+ private disconnect;
159
+ /** Reconnect: at once after a stable connection, else after the backoff (1 s doubling to 10 s). */
160
+ private dropped;
161
+ /** Use `ticket` from now on; re-authenticate the open socket in-band when due, or at once when it
162
+ * says rec and the socket's ticket didn't. */
163
+ private setTicket;
164
+ private setViewers;
165
+ private refuse;
166
+ private status;
167
+ private emit;
168
+ }
@@ -3,6 +3,8 @@ import type { EncryptedEnvelope } from './crypto';
3
3
  import type { LiveColumnsBlock, LiveMessage } from './buffer';
4
4
  import type { LiveDataSource } from '../data/liveDataSource';
5
5
  import type { Dataset } from '../parseDataset';
6
+ import type { VideoTrack } from './videoTrack';
7
+ import type { ClockMap } from './clockMap';
6
8
  /** What a viewer needs to join an encrypted relay stream (from the stream registry). */
7
9
  export interface LiveStreamDescriptor {
8
10
  id: string;
@@ -12,7 +14,7 @@ export interface LiveStreamDescriptor {
12
14
  /** Encrypted marker proving the password is right before any frame arrives. */
13
15
  key_check: EncryptedEnvelope;
14
16
  owner_name?: string;
15
- visibility?: 'private' | 'public' | 'org' | 'project';
17
+ visibility?: 'private' | 'public' | 'org' | 'project' | 'open';
16
18
  org_name?: string | null;
17
19
  /** Null when the viewer can't see the project's name. */
18
20
  project_name?: string | null;
@@ -21,6 +23,12 @@ export interface LiveStreamDescriptor {
21
23
  * owner hosts). The engine's `getToken` is still asked for the credential.
22
24
  */
23
25
  relay_url?: string | null;
26
+ /** A camera stream carries encoded video (KIND_VIDEO), not samples. Default 'data'. */
27
+ kind?: 'data' | 'camera';
28
+ /** A camera's data stream (a camera is a channel of it); null/absent for data streams. */
29
+ parent_stream_id?: string | null;
30
+ /** A camera's low layer (360p, for small panels and walls): its high camera's id; null/absent otherwise. */
31
+ low_of_stream_id?: string | null;
24
32
  }
25
33
  /**
26
34
  * The keys of a stream in an end-to-end encrypted project: frames name their key id
@@ -57,15 +65,25 @@ export interface LiveConnection {
57
65
  id: string;
58
66
  name: string;
59
67
  ownerName: string;
60
- visibility: 'private' | 'public' | 'org' | 'project';
68
+ visibility: 'private' | 'public' | 'org' | 'project' | 'open';
61
69
  orgName: string | null;
62
70
  projectName?: string | null;
71
+ kind: 'data' | 'camera';
72
+ /** A camera's data stream (LiveStreamDescriptor.parent_stream_id); null for data streams. */
73
+ parentId: string | null;
74
+ /** A camera's low layer: its high camera's id (LiveStreamDescriptor.low_of_stream_id); null otherwise. */
75
+ lowOf: string | null;
76
+ /** A camera's encoded video (the same instance for the connection's life); null for data. */
77
+ video: VideoTrack | null;
78
+ /** A data stream's time → wall time map (to sync cameras with it); null for cameras. */
79
+ clock: ClockMap | null;
63
80
  /** Slow (~1 Hz) snapshot for column discovery / count / non-multiband charts. */
64
81
  dataset: Dataset;
65
82
  /** Live decimation source read by the multiband chart every frame (no React). */
66
83
  source: LiveDataSource;
67
84
  rowCount: number;
68
- status: 'connecting' | 'live' | 'stale' | 'error';
85
+ /** 'idle': a camera no panel shows (connected, not downloading). */
86
+ status: 'idle' | 'connecting' | 'live' | 'stale' | 'error';
69
87
  error: string | null;
70
88
  /** When a recording is running: its start time (ms since epoch). */
71
89
  recordingSince: number | null;
@@ -0,0 +1,96 @@
1
+ import type { VideoMessage } from './videoWire';
2
+ /** One complete encoded access unit (H.264), never a decoded frame. */
3
+ export interface EncodedUnit {
4
+ t: number;
5
+ seq: number;
6
+ key: boolean;
7
+ data: Uint8Array;
8
+ arrivedAt: number;
9
+ }
10
+ export interface VideoTrackInfo {
11
+ width: number;
12
+ height: number;
13
+ codec: string;
14
+ latestT: number | null;
15
+ lagMs: number | null;
16
+ units: number;
17
+ bytes: number;
18
+ }
19
+ /**
20
+ * A camera's video as the Camera panel plays it: a live camera connection's ring (kind 'live'), or a camera
21
+ * recorded with a cloud recording (kind 'recorded', RecordedVideoSource). Listed by engine.video.sources().
22
+ */
23
+ export interface VideoSource {
24
+ /** The camera stream's id (the same for its live connection and its recordings). */
25
+ id: string;
26
+ name: string;
27
+ kind: 'live' | 'recorded';
28
+ /** The camera's data stream id; null when unknown. */
29
+ parentId: string | null;
30
+ /** Live: a camera's low layer names its high camera (the Camera panel picks the layer; pickers skip it). */
31
+ lowOf?: string | null;
32
+ /** Recorded: the parent data stream's name, for labels when that stream isn't connected. */
33
+ parentName?: string | null;
34
+ /** Recorded: the data recording it was recorded with (that dataset's info.meta.recordingId). */
35
+ recordingId?: string | null;
36
+ info(): VideoTrackInfo;
37
+ /** Earliest and latest capture times (Unix s), or null. */
38
+ range(): [number, number] | null;
39
+ /** Units to decode to show the frame at capture time `t` (Unix s): the last keyframe <= t through the last unit <= t. */
40
+ seekPlan(t: number): EncodedUnit[] | Promise<EncodedUnit[]>;
41
+ /** Live: the newest keyframe and everything after it. */
42
+ livePlan?(): EncodedUnit[];
43
+ /** Live: each new unit; returns an unsubscribe. */
44
+ onUnit?(fn: (u: EncodedUnit) => void): () => void;
45
+ /** Live: hold the video while it is shown; returns the release. */
46
+ acquire?(): () => void;
47
+ /** Recorded: resolves once the codec is known (info().codec). */
48
+ ready?(): Promise<void>;
49
+ }
50
+ /** A live camera connection's track as a VideoSource (the same object per track while id/name/parent stay). */
51
+ export declare function liveVideoSource(c: {
52
+ id: string;
53
+ name: string;
54
+ parentId: string | null;
55
+ lowOf?: string | null;
56
+ video: VideoTrack;
57
+ }): VideoSource;
58
+ /** Per-camera ring of encoded units: reassembles fragments, bounds memory at GOP boundaries, plans decodes. */
59
+ export declare class VideoTrack {
60
+ private ring;
61
+ private bytes;
62
+ private needKey;
63
+ private width;
64
+ private height;
65
+ private codec;
66
+ private pending;
67
+ private subs;
68
+ private holders;
69
+ private demand;
70
+ private maxBytes;
71
+ private now;
72
+ constructor(maxBytes?: number, now?: () => number);
73
+ /** Feed a decoded KIND_VIDEO message; returns the completed unit, or null while incomplete or discarded. */
74
+ push(m: VideoMessage): EncodedUnit | null;
75
+ /** Drop whole GOPs from the front while over budget; the only GOP is never dropped. */
76
+ private trim;
77
+ /** Units to decode to show the frame at wall time `t` (unix s): last keyframe <= t through last unit <= t. */
78
+ seekPlan(t: number): EncodedUnit[];
79
+ /** The newest keyframe and everything after it. */
80
+ livePlan(): EncodedUnit[];
81
+ /** Called with each completed unit added to the ring. Returns an unsubscribe. */
82
+ onUnit(fn: (u: EncodedUnit) => void): () => void;
83
+ /**
84
+ * Hold the video while it is shown (a Camera panel): the connection downloads only while
85
+ * someone holds it. Returns the release (calling it again does nothing).
86
+ */
87
+ acquire(): () => void;
88
+ /** The connection's hook: called with true when the first holder arrives, false when the last leaves. */
89
+ onDemandChange(fn: (wanted: boolean) => void): void;
90
+ info(): VideoTrackInfo;
91
+ /** Earliest and latest buffered capture times, or null. */
92
+ range(): [number, number] | null;
93
+ /** After a gap (e.g. a rejoin): drop the half-received unit and skip deltas until the next keyframe. The ring stays. */
94
+ resync(): void;
95
+ clear(): void;
96
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Plaintext of a KIND_VIDEO relay message: one H.264 access unit (Annex B), or one
3
+ * fragment of it, stamped with its capture time in Unix seconds. A keyframe's first
4
+ * fragment carries the codec string, so a viewer joining mid-stream configures its
5
+ * decoder from it (Annex B keyframes carry SPS/PPS). Byte-compatible with
6
+ * packages/python/src/kymoscope/wire.py (encode_video / decode_video).
7
+ */
8
+ export declare const VIDEO_VERSION = 1;
9
+ export declare const VIDEO_CODEC_H264 = 1;
10
+ /** Fragments stay under the cloud relay's 1 MiB message limit with room for the header and seal. */
11
+ export declare const VIDEO_MAX_FRAGMENT: number;
12
+ export interface VideoMessage {
13
+ key: boolean;
14
+ last: boolean;
15
+ t: number;
16
+ seq: number;
17
+ fragIndex: number;
18
+ fragCount: number;
19
+ width: number;
20
+ height: number;
21
+ codec: string;
22
+ payload: Uint8Array;
23
+ }
24
+ export declare function encodeVideo(m: VideoMessage): Uint8Array;
25
+ export declare function decodeVideo(b: Uint8Array): VideoMessage | null;
26
+ /** One access unit as the plaintexts to seal, split to VIDEO_MAX_FRAGMENT. */
27
+ export declare function fragmentVideo(au: {
28
+ key: boolean;
29
+ t: number;
30
+ seq: number;
31
+ width: number;
32
+ height: number;
33
+ codec: string;
34
+ data: Uint8Array;
35
+ }): Uint8Array[];
@@ -6,6 +6,10 @@ export declare const KIND_COLUMNS = 3;
6
6
  export declare const KIND_BLOCK = 4;
7
7
  /** The channel table KIND_BLOCK messages refer to (see blockWire.ts). */
8
8
  export declare const KIND_BLOCK_SCHEMA = 5;
9
+ /** One encoded video access unit or a fragment of one (see videoWire.ts). */
10
+ export declare const KIND_VIDEO = 6;
11
+ /** Each channel's last value and its time, against a KIND_BLOCK_SCHEMA (see blockWire.ts). */
12
+ export declare const KIND_LAST_VALUES = 7;
9
13
  export declare const KEY_ID_FLAG = 128;
10
14
  export interface SealedMessage {
11
15
  kind: number;
@@ -372,12 +372,24 @@ export interface GanttPanelChartSettings {
372
372
  gtEndCol: string;
373
373
  gtGroupCol: string;
374
374
  }
375
+ export interface CameraPanelSettings {
376
+ kind: 'camera';
377
+ /** The camera stream's id; '' until chosen. Always the first of `cameras` (kept for settings saved before walls). */
378
+ cameraStreamId: string;
379
+ /** The cameras shown, in order (up to MAX_PANEL_CAMERAS): two or more make a wall. See panelCameras. */
380
+ cameras: string[];
381
+ /** The data stream this camera syncs with; '' = its own stream (the camera's parent) when connected, else the first live data stream. */
382
+ syncStreamId: string;
383
+ /** Manual correction, ms (−10000..10000). */
384
+ offsetMs: number;
385
+ fit: 'contain' | 'cover';
386
+ }
375
387
  /** Settings every chart kind has besides its own. */
376
388
  export interface CommonPanelChartSettings {
377
389
  /** Marks drawn over the chart (text, brush, arrows, boxes, lines). */
378
390
  annotations?: ChartAnnotation[];
379
391
  }
380
- export type PanelChartSettings = CommonPanelChartSettings & (MultibandPanelChartSettings | XyPanelChartSettings | XyzPanelChartSettings | HistogramPanelChartSettings | PolarPanelChartSettings | SpectrumPanelChartSettings | BoxPlotPanelChartSettings | SpectrogramPanelChartSettings | HeatmapPanelChartSettings | ViolinPanelChartSettings | ConfusionPanelChartSettings | RocPanelChartSettings | LossCurvePanelChartSettings | ControlChartPanelChartSettings | ParallelCoordsPanelChartSettings | EmbeddingPanelChartSettings | FlameGraphPanelChartSettings | BarChartPanelChartSettings | RadarPanelChartSettings | AreaChartPanelChartSettings | CdfChartPanelChartSettings | BubbleChartPanelChartSettings | TreemapPanelChartSettings | SankeyPanelChartSettings | CalibrationPanelChartSettings | GaugePanelChartSettings | QQPlotPanelChartSettings | GanttPanelChartSettings);
392
+ export type PanelChartSettings = CommonPanelChartSettings & (MultibandPanelChartSettings | XyPanelChartSettings | XyzPanelChartSettings | HistogramPanelChartSettings | PolarPanelChartSettings | SpectrumPanelChartSettings | BoxPlotPanelChartSettings | SpectrogramPanelChartSettings | HeatmapPanelChartSettings | ViolinPanelChartSettings | ConfusionPanelChartSettings | RocPanelChartSettings | LossCurvePanelChartSettings | ControlChartPanelChartSettings | ParallelCoordsPanelChartSettings | EmbeddingPanelChartSettings | FlameGraphPanelChartSettings | BarChartPanelChartSettings | RadarPanelChartSettings | AreaChartPanelChartSettings | CdfChartPanelChartSettings | BubbleChartPanelChartSettings | TreemapPanelChartSettings | SankeyPanelChartSettings | CalibrationPanelChartSettings | GaugePanelChartSettings | QQPlotPanelChartSettings | GanttPanelChartSettings | CameraPanelSettings);
381
393
  /** Parse optional axis range from min/max strings (same rules as App useMemo). */
382
394
  export declare function parseOptionalAxisRange(minStr: string, maxStr: string): [number, number] | undefined;
383
395
  /** Keep in sync with `savedLayout.ts` defaults. */
@@ -412,8 +424,35 @@ export declare function defaultGaugePanelSettings(): GaugePanelChartSettings;
412
424
  export declare function defaultCalibrationPanelSettings(): CalibrationPanelChartSettings;
413
425
  export declare function defaultQQPlotPanelSettings(): QQPlotPanelChartSettings;
414
426
  export declare function defaultGanttPanelSettings(): GanttPanelChartSettings;
427
+ /** A camera in pickers: "Bench 3 · Camera 1" (its data stream's name: connected, or a recorded camera's parentName),
428
+ * else the camera's own name. */
429
+ export declare function cameraLabel(cam: {
430
+ name: string;
431
+ parentId?: string | null;
432
+ parentName?: string | null;
433
+ }, live: readonly {
434
+ id: string;
435
+ name: string;
436
+ }[]): string;
437
+ export declare const defaultCameraPanelSettings: () => CameraPanelSettings;
438
+ /** A Camera panel shows at most this many cameras (a 3x3 wall). */
439
+ export declare const MAX_PANEL_CAMERAS = 9;
440
+ /** The cameras a Camera panel shows: `cameras` while it starts with cameraStreamId, else just cameraStreamId
441
+ * (a host that sets only cameraStreamId, as before walls, gets that one camera). */
442
+ export declare function panelCameras(s: Pick<CameraPanelSettings, 'cameraStreamId' | 'cameras'>): string[];
415
443
  /** An alarm line from untrusted input, or null. */
416
444
  export declare function coerceAlarmLine(v: unknown): AlarmLine | null;
445
+ /** What a linked line shows for its rule: the rule's threshold, and whether the rule is active now. */
446
+ export interface LinkedRuleLine {
447
+ value: number;
448
+ active: boolean;
449
+ }
450
+ /**
451
+ * Lines linked to rules the runtime holds draw the rule's threshold and state (`linked`). Walks the four alarm
452
+ * places (multiband series, the simple kinds' alarm field, gauge rows, X-Y / X-Y-Z axes). Returns `s` itself when no
453
+ * line resolves, so memoised charts don't redraw.
454
+ */
455
+ export declare function resolveLinkedAlarms(s: PanelChartSettings, rules: ReadonlyMap<string, LinkedRuleLine>): PanelChartSettings;
417
456
  /** The setting holding the alarm line of the chart kinds whose frame draws it. */
418
457
  export declare const CHART_ALARM_FIELDS: {
419
458
  readonly histogram: "histAlarm";
@@ -15,6 +15,7 @@ export interface ExportOptions {
15
15
  export declare const EXPORT_TYPES: {
16
16
  readonly csv: "text/csv";
17
17
  readonly mf4: "application/octet-stream";
18
+ readonly parquet: "application/vnd.apache.parquet";
18
19
  };
19
20
  /**
20
21
  * A recording as a CSV or MF4 file, built in the browser from its encrypted chunks and handed to `write` piece by
@@ -0,0 +1,56 @@
1
+ import { type MessageKey } from '../live/wire';
2
+ import type { EncodedUnit, VideoSource, VideoTrackInfo } from '../live/videoTrack';
3
+ export interface RecordedVideoSourceOptions {
4
+ /** The camera stream's id (what a Camera panel's cameraStreamId names). */
5
+ id: string;
6
+ name: string;
7
+ /** The camera's data stream id. */
8
+ parentId: string | null;
9
+ /** The data stream's name (labels "Stream · Camera" when that stream isn't connected). */
10
+ parentName?: string | null;
11
+ /** The data recording the camera was recorded with: the recording dataset's info.meta.recordingId. */
12
+ recordingId: string;
13
+ /** The camera recording's chunks: relay times (ms since epoch) of each one's first and last message. */
14
+ chunks: {
15
+ seq: number;
16
+ t0: number;
17
+ t1: number;
18
+ }[];
19
+ fetchChunk(seq: number): Promise<ArrayBuffer>;
20
+ /** The camera's key(s). */
21
+ open: MessageKey;
22
+ /** Decoded units kept, in bytes (default 256 MB); least recently used chunks go first. */
23
+ maxBytes?: number;
24
+ }
25
+ export declare class RecordedVideoSource implements VideoSource {
26
+ readonly kind: "recorded";
27
+ readonly id: string;
28
+ readonly name: string;
29
+ readonly parentId: string | null;
30
+ readonly parentName: string | null;
31
+ readonly recordingId: string;
32
+ private chunks;
33
+ private fetch;
34
+ private open;
35
+ private maxBytes;
36
+ /** By chunk index, least recently used first. */
37
+ private cache;
38
+ /** Settled entries: [bytes, units]. */
39
+ private sizes;
40
+ private bytes;
41
+ private width;
42
+ private height;
43
+ private codec;
44
+ private waiting;
45
+ constructor(o: RecordedVideoSourceOptions);
46
+ private parse;
47
+ private parsed;
48
+ private evict;
49
+ /** Chunk i's units in order, its last one finished from the next chunk; [] when it can't be downloaded. */
50
+ private units;
51
+ seekPlan(t: number): Promise<EncodedUnit[]>;
52
+ /** Resolves once the codec is known (the first keyframe of the first chunks), or they had none. */
53
+ ready(): Promise<void>;
54
+ info(): VideoTrackInfo;
55
+ range(): [number, number] | null;
56
+ }
@@ -0,0 +1,7 @@
1
+ /** A 10 px bell, drawn at the label end of an alarm line linked to an alert rule (canvas and SVG share the path). */
2
+ export declare const ALERT_GLYPH_PATH = "M5 .8a3.1 3.1 0 0 0-3.1 3.1v2.3L.8 7.6h8.4L8.1 6.2V3.9A3.1 3.1 0 0 0 5 .8zM3.9 8.4a1.1 1.1 0 0 0 2.2 0z";
3
+ export declare const ALERT_GLYPH_SIZE = 10;
4
+ /** A linked line is dashed while its rule is active (spec §6.1.1); otherwise it keeps its own style. */
5
+ export declare const ACTIVE_DASH: number[];
6
+ /** Paint the bell with its top-left at (x, y). */
7
+ export declare function drawAlertGlyph(ctx: CanvasRenderingContext2D, x: number, y: number, color: string): void;
@@ -4,7 +4,7 @@ export interface GpuSeriesStyle {
4
4
  bandBottom: number;
5
5
  scaleMin: number;
6
6
  scaleMax: number;
7
- /** Median consecutive |Δx| of the column (0 if unknown): drives gap-splitting. */
7
+ /** Mean consecutive |Δx| of the column (0 if unknown): drives gap-splitting (see joinGapThresholdForColumn). */
8
8
  gapMedian: number;
9
9
  /** Line thickness in CSS px; defaults to the renderer's `lineWidth`. */
10
10
  width?: number;
@@ -0,0 +1,85 @@
1
+ import { type CompareOp } from './rules';
2
+ export type AlignAxis = {
3
+ kind: 'elapsed';
4
+ preroll_s?: number;
5
+ } | {
6
+ kind: 'event';
7
+ channel: string;
8
+ op: CompareOp;
9
+ value: number;
10
+ } | {
11
+ kind: 'cumulative';
12
+ channel: string;
13
+ integral?: boolean;
14
+ } | {
15
+ kind: 'clock';
16
+ tz: string;
17
+ };
18
+ export interface AlignedSegment {
19
+ x: Float64Array;
20
+ t: Float64Array;
21
+ cols: Map<string, Float64Array>;
22
+ }
23
+ export interface ReferenceChannel {
24
+ mean: Float32Array;
25
+ sd: Float32Array;
26
+ min: Float32Array;
27
+ max: Float32Array;
28
+ n: Uint16Array;
29
+ }
30
+ export interface ReferenceCurve {
31
+ x: Float64Array;
32
+ channels: Map<string, ReferenceChannel>;
33
+ }
34
+ export interface Comparison {
35
+ /** Per sample of the segment: value − reference mean at its X (NaN where either is missing). */
36
+ delta: Map<string, Float64Array>;
37
+ /** Highest first: mean |delta| / σ (the grid point's σ with ≥ 2 sources, else the channel's spread over the grid). */
38
+ score: Array<{
39
+ channel: string;
40
+ z: number;
41
+ }>;
42
+ /** Runs out of band: from the first sample out to the first sample back in (or the last sample). */
43
+ deviations: Array<{
44
+ t0: number;
45
+ t1: number;
46
+ channels: string[];
47
+ }>;
48
+ }
49
+ export declare const MAX_GRID = 8000;
50
+ /** ponytail: channels × grid points of one reference (2.4 MB raw); split references by channel group if a customer needs more. */
51
+ export declare const MAX_BAND_CELLS = 131072;
52
+ export declare function axisColumn(a: AlignAxis, units?: Record<string, string>): {
53
+ name: string;
54
+ unit: string;
55
+ };
56
+ export declare function localSeconds(t: number, tz: string): number;
57
+ /** X of one segment's samples, fed in time order: history in one pass, live sample by sample. */
58
+ export declare class AxisState {
59
+ #private;
60
+ readonly axis: AlignAxis;
61
+ constructor(axis: AlignAxis);
62
+ /** A segment starts at t0 (resets the state). */
63
+ begin(t0: number): void;
64
+ /** X at time t; `c` is the axis channel's value there (NaN when it has none). NaN when undefined (live: before an event). */
65
+ x(t: number, c: number): number;
66
+ }
67
+ /** A segment's samples (t and NaN-padded columns, e.g. wideDay of its pieces) on `axis`; `tstart` is its start. For
68
+ * `event` the event is searched over the whole segment first, so samples before it get negative X. */
69
+ export declare function alignSegment(t: Float64Array, cols: Map<string, Float64Array>, tstart: number, axis: AlignAxis): AlignedSegment;
70
+ /** y(x) at the grid points: linear between finite neighbours; NaN outside the data or across a gap wider than 4× the
71
+ * median step. Unsorted X (a noisy cumulative tag) is sorted first. */
72
+ export declare function resample(x: Float64Array, y: ArrayLike<number>, grid: Float64Array): Float64Array;
73
+ /** A grid array's value at x (linear; NaN outside the grid or next to a NaN). */
74
+ export declare function gridAt(grid: Float64Array, a: ArrayLike<number>, x: number): number;
75
+ /** The reference of `segs` on a uniform grid of `points` over their X range (one segment: σ 0, n 1). */
76
+ export declare function buildReference(segs: AlignedSegment[], channels: string[], points: number): ReferenceCurve;
77
+ /** A channel's band edges: mean ± k·σ, or min..max. */
78
+ export declare function bandEdges(c: ReferenceChannel, k: number, band: 'sigma' | 'minmax'): {
79
+ lo: Float32Array;
80
+ hi: Float32Array;
81
+ };
82
+ export declare function compareToReference(seg: AlignedSegment, ref: ReferenceCurve, k: number, band?: 'sigma' | 'minmax', minS?: number): Comparison;
83
+ /** A reference curve as bytes (plan Formats "Reference band", zlib). */
84
+ export declare function encodeBand(ref: ReferenceCurve, channels: string[]): Uint8Array;
85
+ export declare function decodeBand(bytes: Uint8Array, channels: string[]): ReferenceCurve;