@sailfish-ai/recorder 1.12.4 → 1.12.6

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 (144) hide show
  1. package/README.md +8 -0
  2. package/dist/canvas/adapters.js +382 -0
  3. package/dist/canvas/adapters.js.br +0 -0
  4. package/dist/canvas/adapters.js.gz +0 -0
  5. package/dist/canvas/canvasManager.js +1949 -0
  6. package/dist/canvas/canvasManager.js.br +0 -0
  7. package/dist/canvas/canvasManager.js.gz +0 -0
  8. package/dist/canvas/canvasPrivacy.js +233 -0
  9. package/dist/canvas/canvasPrivacy.js.br +0 -0
  10. package/dist/canvas/canvasPrivacy.js.gz +0 -0
  11. package/dist/canvas/codec.js +314 -0
  12. package/dist/canvas/codec.js.br +0 -0
  13. package/dist/canvas/codec.js.gz +0 -0
  14. package/dist/canvas/deviceClass.js +67 -0
  15. package/dist/canvas/deviceClass.js.br +0 -0
  16. package/dist/canvas/deviceClass.js.gz +0 -0
  17. package/dist/canvas/encodeWorker.js +284 -0
  18. package/dist/canvas/encodeWorker.js.br +0 -0
  19. package/dist/canvas/encodeWorker.js.gz +0 -0
  20. package/dist/canvas/governor.js +79 -0
  21. package/dist/canvas/governor.js.br +0 -0
  22. package/dist/canvas/governor.js.gz +0 -0
  23. package/dist/canvas/keyframes.js +183 -0
  24. package/dist/canvas/keyframes.js.br +0 -0
  25. package/dist/canvas/keyframes.js.gz +0 -0
  26. package/dist/canvas/mediaRecorderCapture.js +257 -0
  27. package/dist/canvas/mediaRecorderCapture.js.br +0 -0
  28. package/dist/canvas/mediaRecorderCapture.js.gz +0 -0
  29. package/dist/canvas/objectLog.js +150 -0
  30. package/dist/canvas/objectLog.js.br +0 -0
  31. package/dist/canvas/objectLog.js.gz +0 -0
  32. package/dist/canvas/patch2d.js +1302 -0
  33. package/dist/canvas/patch2d.js.br +0 -0
  34. package/dist/canvas/patch2d.js.gz +0 -0
  35. package/dist/canvas/patchGL.js +371 -0
  36. package/dist/canvas/patchGL.js.br +0 -0
  37. package/dist/canvas/patchGL.js.gz +0 -0
  38. package/dist/canvas/patchWebGPU.js +361 -0
  39. package/dist/canvas/patchWebGPU.js.br +0 -0
  40. package/dist/canvas/patchWebGPU.js.gz +0 -0
  41. package/dist/canvas/piiDetect.js +155 -0
  42. package/dist/canvas/piiDetect.js.br +0 -0
  43. package/dist/canvas/piiDetect.js.gz +0 -0
  44. package/dist/canvas/protocol.js +122 -0
  45. package/dist/canvas/protocol.js.br +0 -0
  46. package/dist/canvas/protocol.js.gz +0 -0
  47. package/dist/canvas/videoCapture.js +252 -0
  48. package/dist/canvas/videoCapture.js.br +0 -0
  49. package/dist/canvas/videoCapture.js.gz +0 -0
  50. package/dist/canvas/workerRelay.js +99 -0
  51. package/dist/canvas/workerRelay.js.br +0 -0
  52. package/dist/canvas/workerRelay.js.gz +0 -0
  53. package/dist/canvas/workerShim.js +227 -0
  54. package/dist/canvas/workerShim.js.br +0 -0
  55. package/dist/canvas/workerShim.js.gz +0 -0
  56. package/dist/canvas-worker.cjs +74 -0
  57. package/dist/canvas-worker.cjs.br +0 -0
  58. package/dist/canvas-worker.cjs.gz +0 -0
  59. package/dist/canvas-worker.js +78 -0
  60. package/dist/canvas-worker.js.br +0 -0
  61. package/dist/canvas-worker.js.gz +0 -0
  62. package/dist/canvasFrameStore.js +198 -0
  63. package/dist/canvasFrameStore.js.br +0 -0
  64. package/dist/canvasFrameStore.js.gz +0 -0
  65. package/dist/chunks/canvasManager-BYn3_oyo.js +1440 -0
  66. package/dist/chunks/canvasManager-BYn3_oyo.js.br +0 -0
  67. package/dist/chunks/canvasManager-BYn3_oyo.js.gz +0 -0
  68. package/dist/chunks/canvasManager-CoX5072G.js +1456 -0
  69. package/dist/chunks/canvasManager-CoX5072G.js.br +0 -0
  70. package/dist/chunks/canvasManager-CoX5072G.js.gz +0 -0
  71. package/dist/chunks/{chunkSerializer-FQtY90Av.js → chunkSerializer-C_K0Wyk0.js} +1 -1
  72. package/dist/chunks/chunkSerializer-C_K0Wyk0.js.br +0 -0
  73. package/dist/chunks/chunkSerializer-C_K0Wyk0.js.gz +0 -0
  74. package/dist/chunks/{chunkSerializer-DDukZpgl.js → chunkSerializer-CghPitF4.js} +1 -1
  75. package/dist/chunks/chunkSerializer-CghPitF4.js.br +0 -0
  76. package/dist/chunks/chunkSerializer-CghPitF4.js.gz +0 -0
  77. package/dist/chunks/{index-C-qbsfKe.js → index-BIj4l3CV.js} +1028 -650
  78. package/dist/chunks/index-BIj4l3CV.js.br +0 -0
  79. package/dist/chunks/index-BIj4l3CV.js.gz +0 -0
  80. package/dist/chunks/{index-D6axlCRu.js → index-DIYomvdD.js} +1119 -720
  81. package/dist/chunks/index-DIYomvdD.js.br +0 -0
  82. package/dist/chunks/index-DIYomvdD.js.gz +0 -0
  83. package/dist/chunks/patch2d-9voowhJ_.js +816 -0
  84. package/dist/chunks/patch2d-9voowhJ_.js.br +0 -0
  85. package/dist/chunks/patch2d-9voowhJ_.js.gz +0 -0
  86. package/dist/chunks/patch2d-PQxcaE6r.js +886 -0
  87. package/dist/chunks/patch2d-PQxcaE6r.js.br +0 -0
  88. package/dist/chunks/patch2d-PQxcaE6r.js.gz +0 -0
  89. package/dist/graphql.js +12 -1
  90. package/dist/graphql.js.br +0 -0
  91. package/dist/graphql.js.gz +0 -0
  92. package/dist/inAppReportIssueModal/index.js +48 -0
  93. package/dist/inAppReportIssueModal/index.js.br +0 -0
  94. package/dist/inAppReportIssueModal/index.js.gz +0 -0
  95. package/dist/index.js +144 -2
  96. package/dist/index.js.br +0 -0
  97. package/dist/index.js.gz +0 -0
  98. package/dist/recorder.cjs +2 -2
  99. package/dist/recorder.cjs.br +0 -0
  100. package/dist/recorder.cjs.gz +0 -0
  101. package/dist/recorder.js +52 -41
  102. package/dist/recorder.js.br +0 -0
  103. package/dist/recorder.js.gz +0 -0
  104. package/dist/recorder.umd.cjs +4847 -2212
  105. package/dist/recorder.umd.cjs.br +0 -0
  106. package/dist/recorder.umd.cjs.gz +0 -0
  107. package/dist/recording.js +119 -10
  108. package/dist/recording.js.br +0 -0
  109. package/dist/recording.js.gz +0 -0
  110. package/dist/types/canvas/adapters.d.ts +71 -0
  111. package/dist/types/canvas/canvasManager.d.ts +273 -0
  112. package/dist/types/canvas/canvasPrivacy.d.ts +184 -0
  113. package/dist/types/canvas/codec.d.ts +27 -0
  114. package/dist/types/canvas/deviceClass.d.ts +40 -0
  115. package/dist/types/canvas/encodeWorker.d.ts +54 -0
  116. package/dist/types/canvas/governor.d.ts +44 -0
  117. package/dist/types/canvas/keyframes.d.ts +81 -0
  118. package/dist/types/canvas/mediaRecorderCapture.d.ts +91 -0
  119. package/dist/types/canvas/objectLog.d.ts +75 -0
  120. package/dist/types/canvas/patch2d.d.ts +339 -0
  121. package/dist/types/canvas/patchGL.d.ts +74 -0
  122. package/dist/types/canvas/patchWebGPU.d.ts +37 -0
  123. package/dist/types/canvas/piiDetect.d.ts +60 -0
  124. package/dist/types/canvas/protocol.d.ts +176 -0
  125. package/dist/types/canvas/videoCapture.d.ts +67 -0
  126. package/dist/types/canvas/workerRelay.d.ts +29 -0
  127. package/dist/types/canvas/workerShim.d.ts +10 -0
  128. package/dist/types/canvasFrameStore.d.ts +23 -0
  129. package/dist/types/graphql.d.ts +1 -1
  130. package/dist/types/index.d.ts +99 -1
  131. package/dist/types/types.d.ts +40 -0
  132. package/dist/types/websocket.d.ts +19 -0
  133. package/dist/websocket.js +210 -0
  134. package/dist/websocket.js.br +0 -0
  135. package/dist/websocket.js.gz +0 -0
  136. package/package.json +7 -1
  137. package/dist/chunks/chunkSerializer-DDukZpgl.js.br +0 -0
  138. package/dist/chunks/chunkSerializer-DDukZpgl.js.gz +0 -0
  139. package/dist/chunks/chunkSerializer-FQtY90Av.js.br +0 -0
  140. package/dist/chunks/chunkSerializer-FQtY90Av.js.gz +0 -0
  141. package/dist/chunks/index-C-qbsfKe.js.br +0 -0
  142. package/dist/chunks/index-C-qbsfKe.js.gz +0 -0
  143. package/dist/chunks/index-D6axlCRu.js.br +0 -0
  144. package/dist/chunks/index-D6axlCRu.js.gz +0 -0
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Shared canvas-recording protocol types + the SFCV binary frame format.
3
+ *
4
+ * Types are ported from the validated bench core
5
+ * (test-applications/canvas-recorder-bench/src/capture/patch2d.ts); the SFCV
6
+ * frame builder/parser implements the canvas-recording-pipeline contract §1:
7
+ *
8
+ * bytes 0..3 magic "SFC1" (0x53 0x46 0x43 0x31)
9
+ * bytes 4..7 u32 LE: headerLen
10
+ * bytes 8.. header JSON (utf8, headerLen bytes)
11
+ * rest payload bytes
12
+ *
13
+ * SSR rule: nothing in this module touches window/document/navigator at
14
+ * module scope.
15
+ */
16
+ export type SerializedArg = number | string | boolean | null | {
17
+ $: "asset";
18
+ id: number;
19
+ } | {
20
+ $: "obj";
21
+ id: number;
22
+ } | {
23
+ $: "arr";
24
+ v: number[];
25
+ } | {
26
+ $: "imagedata";
27
+ id: number;
28
+ }
29
+ /** typed-array payload: k = index into TYPED_ARRAY_KINDS, b = raw bytes (copied at call time) */
30
+ | {
31
+ $: "ta";
32
+ k: number;
33
+ b: Uint8Array;
34
+ } | {
35
+ $: "unsupported";
36
+ kind: string;
37
+ };
38
+ export declare const TYPED_ARRAY_KINDS: readonly [Int8ArrayConstructor, Uint8ArrayConstructor, Uint8ClampedArrayConstructor, Int16ArrayConstructor, Uint16ArrayConstructor, Int32ArrayConstructor, Uint32ArrayConstructor, Float32ArrayConstructor, Float64ArrayConstructor];
39
+ /** Copy a typed array / ArrayBuffer into a serialized arg AT CALL TIME. */
40
+ export declare function serializeTypedArray(v: ArrayBufferView | ArrayBuffer): SerializedArg;
41
+ export declare function deserializeTypedArray(a: {
42
+ $: "ta";
43
+ k: number;
44
+ b: Uint8Array;
45
+ }): ArrayBufferView;
46
+ export interface RecordedOp {
47
+ /** EPOCH MILLISECONDS (contract §1 — rrweb-aligned clock). The codec's
48
+ * µs-delta encoding absorbs the large first delta. */
49
+ t: number;
50
+ op: string;
51
+ args: SerializedArg[];
52
+ /** property assignment rather than method call */
53
+ setter?: boolean;
54
+ /** interned-object creation: args = [factoryName, objId, ...factoryArgs] */
55
+ create?: boolean;
56
+ /** method call on interned object: args = [objId, method, ...callArgs] */
57
+ objcall?: boolean;
58
+ /** draw-site stamp id (Phase 3b) — set by __sfDrawSite, carried via the codec __site marker */
59
+ sfId?: string;
60
+ }
61
+ /** Asset payload captured for transmission + size accounting. */
62
+ export interface CapturedAsset {
63
+ id: number;
64
+ kind: "image" | "imagedata";
65
+ /** time first referenced (epoch ms) */
66
+ t: number;
67
+ /** encoded size in bytes (webp for images, deflated raw for imagedata); resolves async */
68
+ bytes: number;
69
+ /** source dimensions (filled when the encode resolves) */
70
+ w?: number;
71
+ h?: number;
72
+ /** retained only when the recorder runs with retainAssets (worker relay path) */
73
+ bitmap?: ImageBitmap;
74
+ imageData?: ImageData;
75
+ }
76
+ /** Encoded-image result from the webp encoder (size always; bytes on demand). */
77
+ export interface EncodedImage {
78
+ size: number;
79
+ bytes?: ArrayBuffer;
80
+ }
81
+ export type EncodeImageFn = (bitmap: ImageBitmap) => Promise<EncodedImage>;
82
+ export interface AssetDataInfo {
83
+ id: number;
84
+ format: "webp" | "imagedata";
85
+ w: number;
86
+ h: number;
87
+ t: number;
88
+ }
89
+ export interface RecorderSink {
90
+ onOp(op: RecordedOp): void;
91
+ /** called when an asset finished encoding (size known) */
92
+ onAssetBytes(id: number, bytes: number, t: number): void;
93
+ note(msg: string): void;
94
+ /** SDK extension: encoded asset payload ready for an SFCV `asset` frame. */
95
+ onAssetData?(info: AssetDataInfo, payload: Uint8Array): void;
96
+ /** SDK extension: a dynamic VIDEO source was captured for a draw (governor signal). */
97
+ onVideoSource?(t: number): void;
98
+ /** SDK extension: a cross-origin/tainted image source could not be read back
99
+ * (canvas is tainted). `t` is the capture-time timestamp (same clock as ops).
100
+ * The manager emits an `op:"taint"` index event so the player can show a
101
+ * placeholder instead of a silent blank. Deduped manager-side per canvas. */
102
+ onTaint?(t: number): void;
103
+ }
104
+ export type AssetMode = "dataurl" | "cas";
105
+ export interface StateFrame {
106
+ props: Record<string, SerializedArg>;
107
+ /** [a,b,c,d,e,f] */
108
+ transform: number[];
109
+ }
110
+ export interface StateSnapshot {
111
+ stack: StateFrame[];
112
+ current: StateFrame;
113
+ }
114
+ export declare class BufferSink implements RecorderSink {
115
+ ops: RecordedOp[];
116
+ assetEvents: {
117
+ id: number;
118
+ bytes: number;
119
+ t: number;
120
+ }[];
121
+ assetData: {
122
+ info: AssetDataInfo;
123
+ payload: Uint8Array;
124
+ }[];
125
+ notes: string[];
126
+ onOp(op: RecordedOp): void;
127
+ onAssetBytes(id: number, bytes: number, t: number): void;
128
+ onAssetData(info: AssetDataInfo, payload: Uint8Array): void;
129
+ note(m: string): void;
130
+ }
131
+ /**
132
+ * Video-mode dirtiness sink: keeps the patch recorder attached purely to
133
+ * drive the per-canvas dirty flag — every op is discarded with ZERO
134
+ * serialize/encode cost (recorders short-circuit on `instanceof DiscardSink`
135
+ * before serializing arguments).
136
+ */
137
+ export declare class DiscardSink implements RecorderSink {
138
+ private onDirty;
139
+ constructor(onDirty: () => void);
140
+ onOp(_op: RecordedOp): void;
141
+ onAssetBytes(_id: number, _bytes: number, _t: number): void;
142
+ note(_m: string): void;
143
+ }
144
+ /** Shared zero-cost op handed to DiscardSink (its contents are never read). */
145
+ export declare const DIRTY_OP: RecordedOp;
146
+ export type SfcvKind = "commands" | "keyframe" | "asset" | "video-init" | "video-chunk";
147
+ export interface SfcvHeader {
148
+ v: 1;
149
+ kind: SfcvKind;
150
+ sessionId: string;
151
+ pageVisitUuid: string;
152
+ /** rrweb mirror node id at start, as a string */
153
+ canvasId: string;
154
+ /** monotonically increasing PER CANVAS across all kinds */
155
+ seq: number;
156
+ /** epoch ms — same clock as rrweb event timestamps */
157
+ tMs: number;
158
+ /**
159
+ * payload byte length. SDK extension to contract §1: each stored .sfcv
160
+ * object is self-delimiting, so a CONCATENATION of frames (the GraphQL
161
+ * dataBase64 retrieval shape, contract §3) can be split without scanning
162
+ * payload bytes for magic. Absent => payload runs to end of buffer
163
+ * (single-frame compatibility).
164
+ */
165
+ pLen?: number;
166
+ meta?: Record<string, unknown>;
167
+ }
168
+ export declare function buildFrame(header: Omit<SfcvHeader, "pLen">, payload?: Uint8Array | ArrayBuffer | null): ArrayBuffer;
169
+ export interface ParsedFrame {
170
+ header: SfcvHeader;
171
+ payload: Uint8Array;
172
+ }
173
+ /** Parse one frame or a concatenation of frames. Throws on corrupt input. */
174
+ export declare function parseFrames(bytes: ArrayBuffer | Uint8Array): ParsedFrame[];
175
+ /** Chunked base64 for ArrayBuffers (video-init decoder descriptions). */
176
+ export declare function bytesToBase64(bytes: ArrayBuffer | Uint8Array): string;
@@ -0,0 +1,67 @@
1
+ export interface VideoCaptureOut {
2
+ /** decoder config known — emit the SFCV video-init frame */
3
+ onInit(codec: string, description: ArrayBuffer | null): void;
4
+ /** encoded chunk — emit an SFCV video-chunk frame */
5
+ onChunk(key: boolean, timestampUs: number, data: ArrayBuffer): void;
6
+ note(msg: string): void;
7
+ /** A11 backpressure: true when the transport's send buffer is saturated, so
8
+ * afterRender SKIPS producing a new VideoFrame until it drains. Optional — when
9
+ * absent, frame production is never throttled. */
10
+ isTransportSaturated?(): boolean;
11
+ }
12
+ export declare class VideoCapture {
13
+ private canvas;
14
+ private now;
15
+ private out;
16
+ private opts?;
17
+ private client;
18
+ private configured;
19
+ private failed;
20
+ private stopped;
21
+ private lastFrame;
22
+ private lastKey;
23
+ private boostUntil;
24
+ private configuredW;
25
+ private configuredH;
26
+ private intervalId;
27
+ /** A11: dedupe the "throttled" note to once per saturation episode */
28
+ private throttledNoted;
29
+ /** A24: an encoder restart at the new canvas size is in flight */
30
+ private restarting;
31
+ /** A24: force the next captured frame to a keyframe (after a restart) */
32
+ private forceKeyNext;
33
+ static supported(): boolean;
34
+ constructor(canvas: HTMLCanvasElement, now: () => number, out: VideoCaptureOut, opts?: {
35
+ intervalPaced?: boolean;
36
+ });
37
+ /** Resolves true when the encoder configured; false -> keyframes-only fallback. */
38
+ start(): Promise<boolean>;
39
+ onInteraction(tMs: number): void;
40
+ /** Same-task capture: invoked by the manager's microtask pump right after
41
+ * the scene rendered. Dirty-by-construction — only called when a frame was
42
+ * drawn (or on the interval for placeholder canvases). */
43
+ afterRender(t: number): void;
44
+ /**
45
+ * A24: restart the encoder at the canvas's current dimensions after a
46
+ * mid-stream resize. The old encoder is torn down (its locked-size frames are
47
+ * no longer valid) and a fresh one is configured for the new size; the new
48
+ * encoder's first chunk re-emits the decoder config (onInit) so the player
49
+ * re-initializes, and the next captured frame is forced to a keyframe. Skips
50
+ * if already restarting, stopped, or the new size is zero.
51
+ */
52
+ private requestRestart;
53
+ /** Non-terminal drain (visibilitychange:hidden on mobile, where the OS may
54
+ * kill the tab without a reliable pagehide): push the encoder's queued frames
55
+ * out as chunks WITHOUT tearing down, so capture resumes if the tab returns.
56
+ * No-op once stopped. */
57
+ flush(): Promise<void>;
58
+ /**
59
+ * A22 / A6 / A10: immediate teardown WITHOUT flushing — used when the canvas
60
+ * was removed from the DOM, masked mid-session, or its GPU context was lost.
61
+ * Flushing would emit chunks for content that is gone/garbage, so we just
62
+ * terminate the encoder worker and stop. Idempotent.
63
+ */
64
+ abort(): void;
65
+ /** Flush the encoder (remaining chunks stream through onChunk) and tear down. */
66
+ stop(): Promise<void>;
67
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Main-thread registry for canvases whose control was transferred to a
3
+ * worker, plus the AUTO-ASSOCIATION patches the bench left to the build
4
+ * plugin (contract §5):
5
+ *
6
+ * - HTMLCanvasElement.prototype.transferControlToOffscreen is patched to
7
+ * remember which OffscreenCanvas came from which canvas element.
8
+ * - Worker.prototype.postMessage is patched to map canvas -> worker when an
9
+ * OffscreenCanvas from a tracked canvas appears in the transfer list.
10
+ *
11
+ * Worker-side recording additionally requires the customer to import
12
+ * `@sailfish-ai/recorder/canvas-worker` in the worker (the shim). The
13
+ * manager pings the worker (sf-record-ping/pong) to detect the shim; absent
14
+ * a pong, the canvas falls back to video capture of the placeholder.
15
+ *
16
+ * SSR rule: nothing here touches window/document at module scope — patches
17
+ * install inside installWorkerRelay().
18
+ */
19
+ /** Manager subscription: fires when a canvas->worker link is discovered. */
20
+ export declare function setWorkerAssociationNotifier(fn: ((canvas: HTMLCanvasElement, worker: Worker) => void) | null): void;
21
+ export declare function installWorkerRelay(): void;
22
+ export declare function registerWorkerCanvas(canvas: HTMLCanvasElement, worker: Worker): void;
23
+ export declare function getWorkerForCanvas(canvas: HTMLCanvasElement): Worker | undefined;
24
+ export declare function unregisterWorkerCanvas(canvas: HTMLCanvasElement): void;
25
+ /**
26
+ * Detect the worker-side shim: resolves true iff the worker answers
27
+ * sf-record-ping with sf-record-pong within `timeoutMs`.
28
+ */
29
+ export declare function pingWorkerShim(worker: Worker, timeoutMs?: number): Promise<boolean>;
@@ -0,0 +1,10 @@
1
+ /** Optional: explicitly register the OffscreenCanvas 2D context to record
2
+ * (the getContext patch normally does this automatically). */
3
+ export declare function registerWorkerCanvasContext(ctx: {
4
+ getTransform(): DOMMatrix;
5
+ }): void;
6
+ /** Optional: keep the recorder clock in sync with a custom render time. */
7
+ export declare function setWorkerNow(now: () => number): void;
8
+ /** Install the relay message listener (call once at worker startup —
9
+ * importing this module inside a worker does it automatically). */
10
+ export declare function installWorkerRecorder(): void;
@@ -0,0 +1,23 @@
1
+ export interface PersistedCanvasFrame {
2
+ id?: number;
3
+ timestamp: number;
4
+ bytes: number;
5
+ buf: ArrayBuffer;
6
+ }
7
+ /**
8
+ * Persist one binary canvas frame. Returns true if the frame was durably
9
+ * written, false if IDB is unavailable (caller should keep the memory-ring
10
+ * fallback). After a successful add, prunes oldest frames so the store stays
11
+ * under FRAME_STORE_MAX_BYTES.
12
+ */
13
+ export declare function persistCanvasFrame(buf: ArrayBuffer): Promise<boolean>;
14
+ /**
15
+ * Read all persisted frames in insertion order (ascending id), dropping any
16
+ * past the TTL and scheduling their background deletion. The returned `buf`s
17
+ * are ready to hand straight to the WS binary send path.
18
+ */
19
+ export declare function readAllCanvasFrames(): Promise<PersistedCanvasFrame[]>;
20
+ export declare function deleteCanvasFramesByIds(ids: number[]): Promise<void>;
21
+ export declare function clearCanvasFrames(): Promise<void>;
22
+ /** Diagnostics: total bytes currently persisted (0 when IDB unavailable). */
23
+ export declare function countPersistedCanvasFrameBytes(): Promise<number>;
@@ -4,7 +4,7 @@ initialBackoff?: number, // Initial backoff in milliseconds
4
4
  backoffFactor?: number): Promise<GraphQLResponse<T>>;
5
5
  export declare function fetchCaptureSettings(apiKey: string, backendApi: string): Promise<GraphQLResponse<CaptureSettingsResponse>>;
6
6
  export declare function fetchFunctionSpanTrackingEnabled(apiKey: string, backendApi: string): Promise<GraphQLResponse<FunctionSpanTrackingEnabledResponse>>;
7
- export declare function startRecordingSession(apiKey: string, recordingId: string, backendApi: string, serviceIdentifier: string, serviceVersion?: string, mapUuid?: string, gitSha?: string, library?: string, serviceAdditionalMetadata?: Record<string, any>): Promise<GraphQLResponse<StartSessionResponse>>;
7
+ export declare function startRecordingSession(apiKey: string, recordingId: string, backendApi: string, serviceIdentifier: string, serviceVersion?: string, mapUuid?: string, gitSha?: string, library?: string, serviceAdditionalMetadata?: Record<string, any>, gitOrg?: string, gitRepo?: string, gitProvider?: string): Promise<GraphQLResponse<StartSessionResponse>>;
8
8
  export declare function sendDomainsToNotPropagateHeaderTo(apiKey: string, domains: string[], backendApi: string): Promise<GraphQLResponse<void>>;
9
9
  export declare function createTriageFromRecorder(apiKey: string, backendApi: string, recordingSessionId: string, timestampStart: string, timestampEnd: string, description?: string, triageSource?: string): Promise<GraphQLResponse<CreateTriageResponse>>;
10
10
  export declare function fetchEngineeringTicketPlatformIntegrations(apiKey: string, backendApi: string): Promise<GraphQLResponse<GetEngineeringTicketPlatformIntegrationsFromApiKeyResponse>>;
@@ -1,13 +1,14 @@
1
1
  import type { LogRecordOptions } from "@sailfish-rrweb/rrweb-plugin-console-record";
2
2
  import type { ShortcutsConfig } from "./inAppReportIssueModal";
3
3
  import { CaptureSettings, MaskInputOptions } from "./types";
4
+ import { type ShapeRedactStyle, type TextRedactStyle } from "./canvas/canvasPrivacy";
4
5
  export declare const STORAGE_VERSION = 1;
5
6
  export declare const DEFAULT_CAPTURE_SETTINGS: CaptureSettings;
6
7
  export declare const DEFAULT_CONSOLE_RECORDING_SETTINGS: LogRecordOptions;
7
8
  export declare function toAbsoluteUrl(url: string): string;
8
9
  export declare function matchUrlWithWildcard(input: unknown, patterns: string[]): boolean;
9
10
  export declare function createSkipHeadersPropagationChecker(domainsToNotPropagateHeaderTo?: string[], domainsToPropagateHeaderTo?: string[]): (url: string) => boolean;
10
- export declare function startRecording({ apiKey, backendApi, domainsToPropagateHeaderTo, domainsToNotPropagateHeaderTo, serviceVersion, serviceIdentifier, gitSha, serviceAdditionalMetadata, enableIpTracking, captureStreamingResponseBody, captureResponseBodyMaxMb, captureStreamPrefixKb, captureStreamTimeoutMs, enableFiberTracking, deferRecording, deferRecordingStart, chunkSnapshot, useWsWorker, capturePerformanceMetrics, maskTextClass, maskInputSelector, maskTextSelector, blockSelector, unmaskSelector, maskInputOptions, library, headlessRecording, }: {
11
+ export declare function startRecording({ apiKey, backendApi, domainsToPropagateHeaderTo, domainsToNotPropagateHeaderTo, serviceVersion, serviceIdentifier, gitSha, serviceAdditionalMetadata, enableIpTracking, captureStreamingResponseBody, captureResponseBodyMaxMb, captureStreamPrefixKb, captureStreamTimeoutMs, enableFiberTracking, deferRecording, deferRecordingStart, chunkSnapshot, useWsWorker, capturePerformanceMetrics, maskTextClass, maskInputSelector, maskTextSelector, blockSelector, unmaskSelector, maskInputOptions, recordCanvas, maskCanvasClass, gitOrg, gitRepo, gitProvider, library, headlessRecording, }: {
11
12
  apiKey: string;
12
13
  backendApi?: string;
13
14
  domainsToPropagateHeaderTo?: string[];
@@ -35,6 +36,11 @@ export declare function startRecording({ apiKey, backendApi, domainsToPropagateH
35
36
  blockSelector?: string;
36
37
  unmaskSelector?: string;
37
38
  maskInputOptions?: MaskInputOptions;
39
+ recordCanvas?: boolean;
40
+ maskCanvasClass?: string;
41
+ gitOrg?: string;
42
+ gitRepo?: string;
43
+ gitProvider?: string;
38
44
  headlessRecording?: boolean;
39
45
  }): Promise<void>;
40
46
  export declare const initRecorder: (options: {
@@ -97,6 +103,32 @@ export declare const initRecorder: (options: {
97
103
  capturePerformanceMetrics?: boolean;
98
104
  showEngTicketFieldsInReportIssueModalDefault?: boolean;
99
105
  maskTextClass?: string;
106
+ /**
107
+ * Enable the Sailfish pixel-perfect **canvas** recorder (2D command stream +
108
+ * WebGL/WebGPU video + keyframes). A caller value wins over the dashboard
109
+ * Capture Setting. Off by default — see the Canvas Recording docs.
110
+ * Distinct from rrweb's legacy `recordCanvas` webp snapshotting, which this
111
+ * SDK force-disables; this option drives the Sailfish pipeline.
112
+ * @default false
113
+ */
114
+ recordCanvas?: boolean;
115
+ /**
116
+ * CSS class that EXCLUDES a `<canvas>` (or any ancestor) from canvas recording
117
+ * entirely — no commands, no keyframes, no video. Whole-canvas masking; for
118
+ * partial masking use `maskCanvasDraws()`.
119
+ * @default "sailfishSanitize"
120
+ */
121
+ maskCanvasClass?: string;
122
+ /**
123
+ * Click-to-code: optional git repo for this build, as a FALLBACK when the
124
+ * `@sailfish-ai/sf-map-utils` build plugin isn't used (the plugin auto-detects
125
+ * the git remote and is the preferred source). Sets the build's org/repo on the
126
+ * ServiceIdentifier so the replay user-journey shows org/repo + file:line
127
+ * deep-links per click. `gitProvider` is GITHUB | GITLAB | BITBUCKET.
128
+ */
129
+ gitOrg?: string;
130
+ gitRepo?: string;
131
+ gitProvider?: string;
100
132
  /**
101
133
  * When true, recording proceeds even inside headless / automated browser
102
134
  * environments (Puppeteer, Playwright, Cypress, etc.), bypassing the
@@ -117,6 +149,72 @@ export declare const initRecorder: (options: {
117
149
  export * from "./graphql";
118
150
  export { openReportIssueModal } from "./inAppReportIssueModal";
119
151
  export * from "./recording";
152
+ /** Per-call redaction options for {@link maskCanvasDraws}. Each field overrides
153
+ * the global style (set via the `canvasRedactStyle` init option) for this block
154
+ * only; omitted fields inherit. */
155
+ export interface MaskCanvasDrawsOptions {
156
+ /** How masked TEXT renders: `"asterisks"` (default — every char → `*`,
157
+ * length-preserving) or `"bar"` (a solid redaction bar over the text). */
158
+ text?: TextRedactStyle;
159
+ /** How masked SHAPES render: `"color"` (default — the SAME shape, recolored to
160
+ * the redaction color), `"block"` (a solid block over the shape's bounding
161
+ * box), or `"remove"` (drop the shape entirely, when the geometry itself is
162
+ * sensitive). */
163
+ shape?: ShapeRedactStyle;
164
+ /** Mask ONLY text inside the block; record shapes/geometry normally. */
165
+ keepShapes?: boolean;
166
+ }
167
+ /**
168
+ * Sanitize SPECIFIC content drawn into a `<canvas>`. Wrap the draw calls that
169
+ * paint sensitive data — geometry, shapes, text, images — and they are **redacted
170
+ * in place**: the recorded command stream draws a redaction *version of the same
171
+ * element* (shapes recolored to a dark redaction color by default, text rendered
172
+ * as length-preserving asterisks), and the masked regions are **blanked on every
173
+ * pixel keyframe** so the real pixels never leave the browser. Layout is
174
+ * preserved; content is hidden. Everything drawn OUTSIDE the wrapper records
175
+ * normally, and the canvas stays pixel-perfect outside the masked regions.
176
+ *
177
+ * This is the only way to mask *part* of a canvas. CSS-selector masking
178
+ * (`data-sf-mask` / `maskCanvasClass` / `maskTextSelector`) is whole-`<canvas>`
179
+ * only — a canvas has no sub-elements for a selector to target, so to redact one
180
+ * shape/region you wrap the draw calls themselves.
181
+ *
182
+ * The redaction style is configurable per call via `opts` (or globally via the
183
+ * `canvasRedactStyle` init option):
184
+ * - `text`: `"asterisks"` (default) | `"bar"`
185
+ * - `shape`: `"color"` (default, same shape recolored) | `"block"` | `"remove"`
186
+ * - `keepShapes`: redact only text, leave shapes visible.
187
+ *
188
+ * Keep the wrapped draws **self-contained** (set the styles/transform you need
189
+ * inside it, or use `ctx.save()`/`ctx.restore()`): masked state setters
190
+ * (fill/stroke styles) are dropped, so a style you change inside but rely on
191
+ * *outside* won't carry over in replay.
192
+ *
193
+ * Safe no-op when canvas recording is off, or when this context isn't being
194
+ * recorded (snippet tier before attach, masked canvas, SSR): it just runs
195
+ * `draw()` and returns its result. 2D contexts only — for WebGL/WebGPU (whose
196
+ * content is pixels, not commands) exclude the whole canvas with `data-sf-mask`.
197
+ *
198
+ * @example
199
+ * import { maskCanvasDraws } from '@sailfish-ai/recorder';
200
+ *
201
+ * function render(ctx) {
202
+ * ctx.fillStyle = '#0a0';
203
+ * ctx.fillRect(0, 0, 800, 600); // recorded
204
+ *
205
+ * maskCanvasDraws(ctx, () => { // redacted in place:
206
+ * ctx.fillStyle = '#007BFF';
207
+ * ctx.fillRect(rectX, rectY, w, h); // ← same rect, recolored to redaction color
208
+ * drawJSTSGeometry(sensitivePolygon, ctx); // ← polygon redacted in place
209
+ * ctx.fillText(patientName, x, y); // ← "***********" (length preserved)
210
+ * });
211
+ *
212
+ * // drop the geometry entirely instead of recoloring it:
213
+ * maskCanvasDraws(ctx, () => drawJSTSGeometry(secretShape, ctx), { shape: 'remove' });
214
+ * }
215
+ */
216
+ export declare function maskCanvasDraws<T>(target: CanvasRenderingContext2D | HTMLCanvasElement, draw: () => T, opts?: MaskCanvasDrawsOptions): T;
217
+ export { registerKonvaStage, registerFabricCanvas, registerChart, registerThreeScene, registerPixiApp, type SemanticTarget, } from "./canvas/adapters";
120
218
  export * from "./sendSailfishMessages";
121
219
  export { getOrSetSessionId } from "./session";
122
220
  export * from "./types";
@@ -17,7 +17,30 @@ export type MaskInputOptions = Partial<{
17
17
  password: boolean;
18
18
  }>;
19
19
  export interface CaptureSettings {
20
+ /**
21
+ * Enable the Sailfish canvas-recording pipeline (SFCV): command/keyframe/video
22
+ * capture of `<canvas>` content shipped as binary frames over the WebSocket.
23
+ * Default FALSE (opt-in). This is the single canvas-recording flag — settable
24
+ * via the dashboard / server payload, or the `initRecorder({ recordCanvas })`
25
+ * option. NB: rrweb's own legacy webp canvas snapshotting is ALWAYS
26
+ * force-disabled at the rrweb `record()` call, so `record()` never receives
27
+ * `recordCanvas: true`; this flag drives ONLY the Sailfish pipeline.
28
+ */
20
29
  recordCanvas: boolean;
30
+ /**
31
+ * @deprecated Backend wire field only (GraphQL `recordCanvasSf` / DB column
32
+ * `record_canvas_sf`). Read ONCE during settings assembly and mapped onto the
33
+ * unified `recordCanvas` above — never read elsewhere in the SDK. Kept so the
34
+ * server `captureSettingsFromApiKey` payload type-checks without renaming the
35
+ * backend field.
36
+ */
37
+ recordCanvasSf?: boolean;
38
+ /**
39
+ * Canvases matching `.${maskCanvasClass}`, `[data-sf-mask]`, or inside a
40
+ * masked subtree record NOTHING (an op:"masked" index event is emitted).
41
+ * Default "sailfishSanitize".
42
+ */
43
+ maskCanvasClass?: string;
21
44
  recordCrossOriginIframes: boolean;
22
45
  collectFonts: boolean;
23
46
  inlineImages: boolean;
@@ -35,6 +58,23 @@ export interface CaptureSettings {
35
58
  blockSelector?: string;
36
59
  unmaskSelector?: string;
37
60
  maskInputOptions?: MaskInputOptions;
61
+ /**
62
+ * Canvas text masking. `true` redacts drawn text on every recorded canvas;
63
+ * `false` opts out of the mirror-DOM default; unset = mirror-DOM (if DOM text
64
+ * masking — `maskTextSelector`/`maskTextClass` — is configured, canvas text is
65
+ * masked too). Independent of `maskCanvasClass` (which suppresses whole
66
+ * canvases).
67
+ */
68
+ maskCanvasText?: boolean;
69
+ /**
70
+ * Global canvas redaction style. Text → `"asterisks"` (default) | `"bar"`;
71
+ * shapes (maskCanvasDraws) → `"color"` (default, same shape recolored) |
72
+ * `"block"` | `"remove"`. Per-call `maskCanvasDraws` options override it.
73
+ */
74
+ canvasRedactStyle?: {
75
+ text?: "asterisks" | "bar";
76
+ shape?: "color" | "block" | "remove";
77
+ };
38
78
  }
39
79
  export interface ConsoleRecordSettings {
40
80
  level: string[];
@@ -3,6 +3,25 @@ export interface WsHandle {
3
3
  readyState: number;
4
4
  close: () => void;
5
5
  }
6
+ /** Count of binary frames dropped from the ring (diagnostics). */
7
+ export declare function getBinaryDropCount(): number;
8
+ /**
9
+ * Current socket send-buffer depth in bytes (best effort). Worker path: the
10
+ * worker posts `bufferedAmount` on a light interval and we cache it. Direct
11
+ * path: read live. 0 when the socket is closed/unavailable.
12
+ */
13
+ export declare function getBufferedAmount(): number;
14
+ /** True when the socket's send buffer is over the saturation threshold —
15
+ * callers (canvas video capture) should pause producing new frames. */
16
+ export declare function isTransportSaturated(): boolean;
17
+ /**
18
+ * Send a binary frame (canvas SFCV) over the recording WebSocket.
19
+ * Direct path: webSocket.send(ArrayBuffer); worker path: transferred
20
+ * postMessage. When the socket is not open, the frame is buffered durably in
21
+ * IndexedDB (surviving reload) with a memory-ring fallback, and flushed on
22
+ * reconnect.
23
+ */
24
+ export declare function wsSendBinary(buffer: ArrayBuffer): boolean;
6
25
  export declare function requestTimeSync(): void;
7
26
  type NavigationCallback = () => void;
8
27
  /** Register a callback to be invoked immediately on pushState/replaceState/popstate/hashchange. */