@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
package/dist/graphql.js CHANGED
@@ -29,6 +29,7 @@ export function fetchCaptureSettings(apiKey, backendApi) {
29
29
  query GetCaptureSettingsFromApiKey($apiKey: String!) {
30
30
  captureSettingsFromApiKey(apiKey: $apiKey) {
31
31
  recordCanvas
32
+ recordCanvasSf
32
33
  recordCrossOriginIframes
33
34
  collectFonts
34
35
  inlineImages
@@ -44,6 +45,7 @@ export function fetchCaptureSettings(apiKey, backendApi) {
44
45
  blockSelector
45
46
  unmaskSelector
46
47
  maskInputOptions
48
+ maskCanvasText
47
49
  }
48
50
  }
49
51
  `, { apiKey, backendApi });
@@ -55,7 +57,7 @@ export function fetchFunctionSpanTrackingEnabled(apiKey, backendApi) {
55
57
  }
56
58
  `, { apiKey, backendApi });
57
59
  }
58
- export function startRecordingSession(apiKey, recordingId, backendApi, serviceIdentifier, serviceVersion, mapUuid, gitSha, library, serviceAdditionalMetadata) {
60
+ export function startRecordingSession(apiKey, recordingId, backendApi, serviceIdentifier, serviceVersion, mapUuid, gitSha, library, serviceAdditionalMetadata, gitOrg, gitRepo, gitProvider) {
59
61
  // These must be sent as None/Null for now (not user-provided)
60
62
  const startRecordingFilePath = null;
61
63
  const startRecordingLineNumber = null;
@@ -66,6 +68,9 @@ export function startRecordingSession(apiKey, recordingId, backendApi, serviceId
66
68
  $serviceVersion: String,
67
69
  $mapUuid: String,
68
70
  $gitSha: String,
71
+ $gitOrg: String,
72
+ $gitRepo: String,
73
+ $gitProvider: String,
69
74
  $library: String,
70
75
  $serviceAdditionalMetadata: JSON,
71
76
  $startRecordingFilePath: String,
@@ -78,6 +83,9 @@ export function startRecordingSession(apiKey, recordingId, backendApi, serviceId
78
83
  serviceVersion: $serviceVersion,
79
84
  mapUuid: $mapUuid,
80
85
  gitSha: $gitSha,
86
+ gitOrg: $gitOrg,
87
+ gitRepo: $gitRepo,
88
+ gitProvider: $gitProvider,
81
89
  library: $library,
82
90
  serviceAdditionalMetadata: $serviceAdditionalMetadata,
83
91
  startRecordingFilePath: $startRecordingFilePath,
@@ -93,6 +101,9 @@ export function startRecordingSession(apiKey, recordingId, backendApi, serviceId
93
101
  serviceVersion,
94
102
  mapUuid,
95
103
  gitSha,
104
+ gitOrg,
105
+ gitRepo,
106
+ gitProvider,
96
107
  library,
97
108
  serviceAdditionalMetadata,
98
109
  startRecordingFilePath,
Binary file
Binary file
@@ -1283,6 +1283,54 @@ function bindListeners() {
1283
1283
  const sprintFieldId = getSprintFieldId();
1284
1284
  engTicketCustomFields[sprintFieldId] = parseInt(sprintValue, 10);
1285
1285
  }
1286
+ // Block submit when a required eng-ticket field that's actually
1287
+ // rendered has no value. We only flag fields the user can fix:
1288
+ // renderDynamicField returns null (no DOM input) for fields like a
1289
+ // user picker with no assignable users, so absence of a
1290
+ // [data-field-id] element means the field isn't on screen and is
1291
+ // skipped. The title is already validated above; priority always has
1292
+ // a default selection. (Done before closeModal so the inputs still
1293
+ // exist to probe.)
1294
+ const isFieldEmpty = (v) => {
1295
+ if (v === undefined || v === null)
1296
+ return true;
1297
+ if (typeof v === "string")
1298
+ return v.trim() === "";
1299
+ if (Array.isArray(v))
1300
+ return v.length === 0;
1301
+ return false;
1302
+ };
1303
+ const requiredFields = (getFieldsForProject(engTicketProject, engTicketIssueType) || []).filter((f) => f && f.required);
1304
+ const missingRequired = [];
1305
+ for (const f of requiredFields) {
1306
+ const fid = f.fieldId || f.key;
1307
+ if (fid === "summary" || fid === "description" || fid === "priority") {
1308
+ continue;
1309
+ }
1310
+ let rendered;
1311
+ let value;
1312
+ if (fid === "project") {
1313
+ rendered = !!document.getElementById("sf-eng-ticket-project");
1314
+ value = engTicketProject;
1315
+ }
1316
+ else if (fid === "issuetype") {
1317
+ rendered = !!document.getElementById("sf-eng-ticket-type");
1318
+ value = engTicketIssueType;
1319
+ }
1320
+ else {
1321
+ // Dynamic field — only flag when renderDynamicField produced an
1322
+ // input (its container/control carries data-field-id).
1323
+ rendered = !!document.querySelector(`[data-field-id="${fid}"]`);
1324
+ value = engTicketCustomFields[fid];
1325
+ }
1326
+ if (rendered && isFieldEmpty(value)) {
1327
+ missingRequired.push(f.name || fid);
1328
+ }
1329
+ }
1330
+ if (missingRequired.length > 0) {
1331
+ alert(`Please fill in the required field${missingRequired.length > 1 ? "s" : ""}: ${missingRequired.join(", ")}`);
1332
+ return;
1333
+ }
1286
1334
  closeModal();
1287
1335
  // Create triage + issue (with optional engineering ticket)
1288
1336
  createTriageAndIssue(`${startTimestamp}`, `${endTimestamp}`, desc, issueName, issueDescription, currentState.createEngTicket, engTicketTeam, engTicketProject, engTicketPriority, engTicketLabels, engTicketIssueType, engTicketCustomFields, mode === "startnow" ? "RECORDED" : "LOOKBACK");
package/dist/index.js CHANGED
@@ -18,6 +18,9 @@ import { onNavigationChange } from "./websocket";
18
18
  import { clearStaleFuncSpanState, getFuncSpanHeader, isFunctionSpanTrackingEnabled, requestTimeSync, restoreFuncSpanState, sendEvent, sendMessage, } from "./websocket";
19
19
  import { yieldToMain } from "./scheduler";
20
20
  import { detectFramework } from "./frameworkDetection";
21
+ import { installEarlyObjectLogging } from "./canvas/objectLog";
22
+ import { recorderForContext, notePreAttachMaskUsed } from "./canvas/patch2d";
23
+ import { resolveRedactStyle, } from "./canvas/canvasPrivacy";
21
24
  const DEBUG = readDebugFlag(); // A wrapper around fetch that suppresses connection refused errors
22
25
  // Regex cache for matchUrlWithWildcard() — avoids recompiling on every network request
23
26
  const _regexCache = new Map();
@@ -1093,7 +1096,7 @@ export async function startRecording({ apiKey, backendApi = "https://api-service
1093
1096
  // Pass [] to fully disable header propagation. Pass exact patterns
1094
1097
  // (e.g. ["api.myapp.com", "*.internal.com"]) to restrict propagation
1095
1098
  // to a known set of domains.
1096
- domainsToPropagateHeaderTo = ["*"], domainsToNotPropagateHeaderTo = [], serviceVersion, serviceIdentifier, gitSha, serviceAdditionalMetadata, enableIpTracking, captureStreamingResponseBody = true, captureResponseBodyMaxMb = 10, captureStreamPrefixKb = 64, captureStreamTimeoutMs = 10000, enableFiberTracking = false, deferRecording, deferRecordingStart, chunkSnapshot, useWsWorker = true, capturePerformanceMetrics = true, maskTextClass, maskInputSelector, maskTextSelector, blockSelector, unmaskSelector, maskInputOptions, library, headlessRecording = false, }) {
1099
+ domainsToPropagateHeaderTo = ["*"], domainsToNotPropagateHeaderTo = [], serviceVersion, serviceIdentifier, gitSha, serviceAdditionalMetadata, enableIpTracking, captureStreamingResponseBody = true, captureResponseBodyMaxMb = 10, captureStreamPrefixKb = 64, captureStreamTimeoutMs = 10000, enableFiberTracking = false, deferRecording, deferRecordingStart, chunkSnapshot, useWsWorker = true, capturePerformanceMetrics = true, maskTextClass, maskInputSelector, maskTextSelector, blockSelector, unmaskSelector, maskInputOptions, recordCanvas, maskCanvasClass, gitOrg, gitRepo, gitProvider, library, headlessRecording = false, }) {
1097
1100
  // Synthetic-environment no-op: Lighthouse/PSI, HeadlessChrome, WebPageTest
1098
1101
  // (PTST), Puppeteer/Playwright/Selenium (navigator.webdriver). We skip init
1099
1102
  // entirely to avoid WSS retry noise, third-party perf penalties in audits,
@@ -1146,6 +1149,27 @@ domainsToPropagateHeaderTo = ["*"], domainsToNotPropagateHeaderTo = [], serviceV
1146
1149
  setupFetchInterceptor(domainsToNotPropagateHeaderTo, bodyCaptureConfig, domainsToPropagateHeaderTo);
1147
1150
  g.fetchPatched = true;
1148
1151
  }
1152
+ // 1b. Canvas object-creation logging (recordCanvas opt-in). Gradients,
1153
+ // patterns, and Path2Ds are OPAQUE — they can only be captured at
1154
+ // construction time, and app scene code typically constructs them once at
1155
+ // startup, BEFORE the settings fetch + session start complete and
1156
+ // startCanvasRecording() installs the full capture hooks. Install the
1157
+ // lightweight creation log NOW, synchronously, so those objects replay
1158
+ // (Phase 0.5 E2E root cause on /2d-objects). A caller-passed
1159
+ // `recordCanvas` (or the compiled DEFAULT_CAPTURE_SETTINGS flag) is the
1160
+ // only signal available this early — the server setting hasn't been
1161
+ // fetched yet. When ONLY the server enables it, hooks still install at
1162
+ // startCanvasRecording() with the documented pre-existing-object limitation.
1163
+ const earlyRecordCanvas = recordCanvas ?? DEFAULT_CAPTURE_SETTINGS.recordCanvas;
1164
+ if (!g.canvasObjLogInit && earlyRecordCanvas) {
1165
+ try {
1166
+ installEarlyObjectLogging();
1167
+ }
1168
+ catch {
1169
+ /* never break host-app init */
1170
+ }
1171
+ g.canvasObjLogInit = true;
1172
+ }
1149
1173
  await yieldToMain();
1150
1174
  // 1c. DOM content events
1151
1175
  if (!g.domEventsInit) {
@@ -1240,7 +1264,7 @@ domainsToPropagateHeaderTo = ["*"], domainsToNotPropagateHeaderTo = [], serviceV
1240
1264
  // Fire both GraphQL calls in parallel — they are independent
1241
1265
  const [captureSettingsResponse, sessionResponse] = await Promise.all([
1242
1266
  fetchCaptureSettings(apiKey, backendApi),
1243
- startRecordingSession(apiKey, sessionId, backendApi, effectiveServiceIdentifier, effectiveServiceVersion, effectiveMapUuid, effectiveGitSha, effectiveLibrary, metadataWithFramework),
1267
+ startRecordingSession(apiKey, sessionId, backendApi, effectiveServiceIdentifier, effectiveServiceVersion, effectiveMapUuid, effectiveGitSha, effectiveLibrary, metadataWithFramework, gitOrg, gitRepo, gitProvider),
1244
1268
  ]);
1245
1269
  const captureSettings = {
1246
1270
  ...DEFAULT_CAPTURE_SETTINGS,
@@ -1255,6 +1279,16 @@ domainsToPropagateHeaderTo = ["*"], domainsToNotPropagateHeaderTo = [], serviceV
1255
1279
  ...(blockSelector !== undefined ? { blockSelector } : {}),
1256
1280
  ...(unmaskSelector !== undefined ? { unmaskSelector } : {}),
1257
1281
  ...(maskInputOptions !== undefined ? { maskInputOptions } : {}),
1282
+ // Sailfish canvas recorder — the single `recordCanvas` flag. Precedence:
1283
+ // caller initRecorder option > server `recordCanvasSf` column (the backend
1284
+ // field name is unchanged) > default. Set explicitly AFTER the server
1285
+ // spread so it maps the backend's `recordCanvasSf` onto the SDK's unified
1286
+ // `recordCanvas`. rrweb's own legacy `recordCanvas` is force-disabled at
1287
+ // the record() call, so this flag drives ONLY the Sailfish pipeline.
1288
+ recordCanvas: recordCanvas ??
1289
+ captureSettingsResponse.data?.captureSettingsFromApiKey?.recordCanvasSf ??
1290
+ DEFAULT_CAPTURE_SETTINGS.recordCanvas,
1291
+ ...(maskCanvasClass !== undefined ? { maskCanvasClass } : {}),
1258
1292
  };
1259
1293
  // If a socket is already open now, stop here.
1260
1294
  if (g.ws && g.ws.readyState === 1) {
@@ -1361,6 +1395,114 @@ export const initRecorder = async (options) => {
1361
1395
  export * from "./graphql";
1362
1396
  export { openReportIssueModal } from "./inAppReportIssueModal";
1363
1397
  export * from "./recording";
1398
+ /**
1399
+ * Sanitize SPECIFIC content drawn into a `<canvas>`. Wrap the draw calls that
1400
+ * paint sensitive data — geometry, shapes, text, images — and they are **redacted
1401
+ * in place**: the recorded command stream draws a redaction *version of the same
1402
+ * element* (shapes recolored to a dark redaction color by default, text rendered
1403
+ * as length-preserving asterisks), and the masked regions are **blanked on every
1404
+ * pixel keyframe** so the real pixels never leave the browser. Layout is
1405
+ * preserved; content is hidden. Everything drawn OUTSIDE the wrapper records
1406
+ * normally, and the canvas stays pixel-perfect outside the masked regions.
1407
+ *
1408
+ * This is the only way to mask *part* of a canvas. CSS-selector masking
1409
+ * (`data-sf-mask` / `maskCanvasClass` / `maskTextSelector`) is whole-`<canvas>`
1410
+ * only — a canvas has no sub-elements for a selector to target, so to redact one
1411
+ * shape/region you wrap the draw calls themselves.
1412
+ *
1413
+ * The redaction style is configurable per call via `opts` (or globally via the
1414
+ * `canvasRedactStyle` init option):
1415
+ * - `text`: `"asterisks"` (default) | `"bar"`
1416
+ * - `shape`: `"color"` (default, same shape recolored) | `"block"` | `"remove"`
1417
+ * - `keepShapes`: redact only text, leave shapes visible.
1418
+ *
1419
+ * Keep the wrapped draws **self-contained** (set the styles/transform you need
1420
+ * inside it, or use `ctx.save()`/`ctx.restore()`): masked state setters
1421
+ * (fill/stroke styles) are dropped, so a style you change inside but rely on
1422
+ * *outside* won't carry over in replay.
1423
+ *
1424
+ * Safe no-op when canvas recording is off, or when this context isn't being
1425
+ * recorded (snippet tier before attach, masked canvas, SSR): it just runs
1426
+ * `draw()` and returns its result. 2D contexts only — for WebGL/WebGPU (whose
1427
+ * content is pixels, not commands) exclude the whole canvas with `data-sf-mask`.
1428
+ *
1429
+ * @example
1430
+ * import { maskCanvasDraws } from '@sailfish-ai/recorder';
1431
+ *
1432
+ * function render(ctx) {
1433
+ * ctx.fillStyle = '#0a0';
1434
+ * ctx.fillRect(0, 0, 800, 600); // recorded
1435
+ *
1436
+ * maskCanvasDraws(ctx, () => { // redacted in place:
1437
+ * ctx.fillStyle = '#007BFF';
1438
+ * ctx.fillRect(rectX, rectY, w, h); // ← same rect, recolored to redaction color
1439
+ * drawJSTSGeometry(sensitivePolygon, ctx); // ← polygon redacted in place
1440
+ * ctx.fillText(patientName, x, y); // ← "***********" (length preserved)
1441
+ * });
1442
+ *
1443
+ * // drop the geometry entirely instead of recoloring it:
1444
+ * maskCanvasDraws(ctx, () => drawJSTSGeometry(secretShape, ctx), { shape: 'remove' });
1445
+ * }
1446
+ */
1447
+ export function maskCanvasDraws(target, draw, opts) {
1448
+ let ctx = null;
1449
+ try {
1450
+ if (typeof HTMLCanvasElement !== "undefined" && target instanceof HTMLCanvasElement) {
1451
+ ctx = target.getContext("2d");
1452
+ }
1453
+ else {
1454
+ ctx = target;
1455
+ }
1456
+ }
1457
+ catch {
1458
+ ctx = null;
1459
+ }
1460
+ const rec = ctx ? recorderForContext(ctx) : undefined;
1461
+ if (!rec) {
1462
+ // Recording off / canvas not recorded: the draw still paints REAL pixels with
1463
+ // no recorder to track a blank region. Flag THIS canvas so its A27 initial
1464
+ // keyframe is skipped — it could otherwise ship these masked pixels
1465
+ // un-blanked. [audit A27] Scoped per-canvas (WeakSet) so one off-recording
1466
+ // mask doesn't disable A27 for every other canvas session-wide. [audit H2]
1467
+ let maskedCanvas = null;
1468
+ try {
1469
+ if (typeof HTMLCanvasElement !== "undefined" && target instanceof HTMLCanvasElement) {
1470
+ maskedCanvas = target;
1471
+ }
1472
+ else {
1473
+ const c = ctx?.canvas;
1474
+ if (typeof HTMLCanvasElement !== "undefined" && c instanceof HTMLCanvasElement)
1475
+ maskedCanvas = c;
1476
+ }
1477
+ }
1478
+ catch {
1479
+ maskedCanvas = null;
1480
+ }
1481
+ notePreAttachMaskUsed(maskedCanvas);
1482
+ return draw();
1483
+ }
1484
+ // Resolve the per-call style over the recorder's current style; save/restore
1485
+ // so nested maskCanvasDraws (and the global default) are honored on exit.
1486
+ const prevStyle = rec.redactStyle;
1487
+ const prevTextOnly = rec.textOnlyMask;
1488
+ if (opts) {
1489
+ rec.redactStyle = resolveRedactStyle(prevStyle, { text: opts.text, shape: opts.shape });
1490
+ if (opts.keepShapes !== undefined)
1491
+ rec.textOnlyMask = opts.keepShapes;
1492
+ }
1493
+ rec.maskDepth++;
1494
+ try {
1495
+ return draw();
1496
+ }
1497
+ finally {
1498
+ rec.maskDepth--;
1499
+ rec.redactStyle = prevStyle;
1500
+ rec.textOnlyMask = prevTextOnly;
1501
+ }
1502
+ }
1503
+ // Phase 4 framework semantic adapters (contract §9): customers register their
1504
+ // live framework instances so canvas clicks resolve to logical objects.
1505
+ export { registerKonvaStage, registerFabricCanvas, registerChart, registerThreeScene, registerPixiApp, } from "./canvas/adapters";
1364
1506
  export * from "./sendSailfishMessages";
1365
1507
  export { getOrSetSessionId } from "./session";
1366
1508
  export * from "./types";
package/dist/index.js.br CHANGED
Binary file
package/dist/index.js.gz CHANGED
Binary file
package/dist/recorder.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const e = require("./chunks/index-C-qbsfKe.js");
4
- exports.DEFAULT_CAPTURE_SETTINGS = e.DEFAULT_CAPTURE_SETTINGS, exports.DEFAULT_CONSOLE_RECORDING_SETTINGS = e.DEFAULT_CONSOLE_RECORDING_SETTINGS, exports.STORAGE_VERSION = e.STORAGE_VERSION, exports.addOrUpdateMetadata = e.addOrUpdateMetadata, exports.buildBatches = e.buildBatches, exports.clearStaleFuncSpanState = e.clearStaleFuncSpanState, exports.createSkipHeadersPropagationChecker = e.createSkipHeadersPropagationChecker, exports.createTriageAndIssueFromRecorder = e.createTriageAndIssueFromRecorder, exports.createTriageFromRecorder = e.createTriageFromRecorder, exports.disableFunctionSpanTracking = e.disableFunctionSpanTracking, exports.enableFunctionSpanTracking = e.enableFunctionSpanTracking, exports.ensureHrefCache = e.ensureHrefCache, exports.eventSize = e.eventSize, exports.fetchAndSendIp = e.fetchAndSendIp, exports.fetchCaptureSettings = e.fetchCaptureSettings, exports.fetchEngineeringTicketPlatformIntegrations = e.fetchEngineeringTicketPlatformIntegrations, exports.fetchFunctionSpanTrackingEnabled = e.fetchFunctionSpanTrackingEnabled, exports.flushBufferedEvents = e.flushBufferedEvents, exports.getCachedHref = e.getCachedHref, exports.getCachedHrefNoQuery = e.getCachedHrefNoQuery, exports.getFuncSpanHeader = e.getFuncSpanHeader, exports.getIdentifiedUser = e.getIdentifiedUser, exports.getOrSetSessionId = e.getOrSetSessionId, exports.getUrlAndStoredUuids = e.getUrlAndStoredUuids, exports.identify = e.identify, exports.initRecorder = e.initRecorder, exports.initializeConsolePlugin = e.initializeConsolePlugin, exports.initializeDomContentEvents = e.initializeDomContentEvents, exports.initializeFunctionSpanTrackingFromApi = e.initializeFunctionSpanTrackingFromApi, exports.initializePerformancePlugin = e.initializePerformancePlugin, exports.initializeRecording = e.initializeRecording, exports.initializeWebSocket = e.initializeWebSocket, exports.invalidateUrlCache = e.invalidateUrlCache, exports.isFunctionSpanTrackingEnabled = e.isFunctionSpanTrackingEnabled, exports.maskInputFn = e.maskInputFn, exports.matchUrlWithWildcard = e.matchUrlWithWildcard, Object.defineProperty(exports, "nowTimestamp", { enumerable: true, get: () => e.nowTimestamp }), exports.onNavigationChange = e.onNavigationChange, exports.openReportIssueModal = e.openReportIssueModal, exports.requestTimeSync = e.requestTimeSync, exports.restoreFuncSpanState = e.restoreFuncSpanState, exports.sendDomainsToNotPropagateHeaderTo = e.sendDomainsToNotPropagateHeaderTo, exports.sendEvent = e.sendEvent, exports.sendGraphQLRequest = e.sendGraphQLRequest, exports.sendMessage = e.sendMessage, exports.startRecording = e.startRecording, exports.startRecordingSession = e.startRecordingSession, exports.toAbsoluteUrl = e.toAbsoluteUrl, exports.trackingEvent = e.trackingEvent, exports.withAppUrlMetadata = e.withAppUrlMetadata;
3
+ const e = require("./chunks/index-BIj4l3CV.js");
4
+ require("./chunks/patch2d-9voowhJ_.js"), exports.DEFAULT_CAPTURE_SETTINGS = e.DEFAULT_CAPTURE_SETTINGS, exports.DEFAULT_CONSOLE_RECORDING_SETTINGS = e.DEFAULT_CONSOLE_RECORDING_SETTINGS, exports.STORAGE_VERSION = e.STORAGE_VERSION, exports.addOrUpdateMetadata = e.addOrUpdateMetadata, exports.buildBatches = e.buildBatches, exports.clearStaleFuncSpanState = e.clearStaleFuncSpanState, exports.createSkipHeadersPropagationChecker = e.createSkipHeadersPropagationChecker, exports.createTriageAndIssueFromRecorder = e.createTriageAndIssueFromRecorder, exports.createTriageFromRecorder = e.createTriageFromRecorder, exports.disableFunctionSpanTracking = e.disableFunctionSpanTracking, exports.enableFunctionSpanTracking = e.enableFunctionSpanTracking, exports.ensureHrefCache = e.ensureHrefCache, exports.eventSize = e.eventSize, exports.fetchAndSendIp = e.fetchAndSendIp, exports.fetchCaptureSettings = e.fetchCaptureSettings, exports.fetchEngineeringTicketPlatformIntegrations = e.fetchEngineeringTicketPlatformIntegrations, exports.fetchFunctionSpanTrackingEnabled = e.fetchFunctionSpanTrackingEnabled, exports.flushBufferedEvents = e.flushBufferedEvents, exports.getBinaryDropCount = e.getBinaryDropCount, exports.getBufferedAmount = e.getBufferedAmount, exports.getCachedHref = e.getCachedHref, exports.getCachedHrefNoQuery = e.getCachedHrefNoQuery, exports.getFuncSpanHeader = e.getFuncSpanHeader, exports.getIdentifiedUser = e.getIdentifiedUser, exports.getOrSetSessionId = e.getOrSetSessionId, exports.getUrlAndStoredUuids = e.getUrlAndStoredUuids, exports.identify = e.identify, exports.initRecorder = e.initRecorder, exports.initializeConsolePlugin = e.initializeConsolePlugin, exports.initializeDomContentEvents = e.initializeDomContentEvents, exports.initializeFunctionSpanTrackingFromApi = e.initializeFunctionSpanTrackingFromApi, exports.initializePerformancePlugin = e.initializePerformancePlugin, exports.initializeRecording = e.initializeRecording, exports.initializeWebSocket = e.initializeWebSocket, exports.invalidateUrlCache = e.invalidateUrlCache, exports.isFunctionSpanTrackingEnabled = e.isFunctionSpanTrackingEnabled, exports.isTransportSaturated = e.isTransportSaturated, exports.maskCanvasDraws = e.maskCanvasDraws, exports.maskInputFn = e.maskInputFn, exports.matchUrlWithWildcard = e.matchUrlWithWildcard, Object.defineProperty(exports, "nowTimestamp", { enumerable: true, get: () => e.nowTimestamp }), exports.onNavigationChange = e.onNavigationChange, exports.openReportIssueModal = e.openReportIssueModal, exports.registerChart = e.registerChart, exports.registerFabricCanvas = e.registerFabricCanvas, exports.registerKonvaStage = e.registerKonvaStage, exports.registerPixiApp = e.registerPixiApp, exports.registerThreeScene = e.registerThreeScene, exports.requestTimeSync = e.requestTimeSync, exports.restoreFuncSpanState = e.restoreFuncSpanState, exports.sendDomainsToNotPropagateHeaderTo = e.sendDomainsToNotPropagateHeaderTo, exports.sendEvent = e.sendEvent, exports.sendGraphQLRequest = e.sendGraphQLRequest, exports.sendMessage = e.sendMessage, exports.startRecording = e.startRecording, exports.startRecordingSession = e.startRecordingSession, exports.toAbsoluteUrl = e.toAbsoluteUrl, exports.trackingEvent = e.trackingEvent, exports.withAppUrlMetadata = e.withAppUrlMetadata, exports.wsSendBinary = e.wsSendBinary;
Binary file
Binary file
package/dist/recorder.js CHANGED
@@ -1,4 +1,5 @@
1
- import { D, a, S, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, z, A, B, C, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, T, U, V, W, X, Y } from "./chunks/index-D6axlCRu.js";
1
+ import { D, a, S, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s, t, v, w, x, z, A, B, C, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, T, U, V, W, X, Y, Z, _, $, a0, a1, a2, a3, a4, a5, a6, a7, a8 } from "./chunks/index-DIYomvdD.js";
2
+ import "./chunks/patch2d-PQxcaE6r.js";
2
3
  export {
3
4
  D as DEFAULT_CAPTURE_SETTINGS,
4
5
  a as DEFAULT_CONSOLE_RECORDING_SETTINGS,
@@ -10,44 +11,54 @@ export {
10
11
  f as createTriageAndIssueFromRecorder,
11
12
  g as createTriageFromRecorder,
12
13
  h as disableFunctionSpanTracking,
13
- i as enableFunctionSpanTracking,
14
- j as ensureHrefCache,
15
- k as eventSize,
16
- l as fetchAndSendIp,
17
- m as fetchCaptureSettings,
18
- n as fetchEngineeringTicketPlatformIntegrations,
19
- o as fetchFunctionSpanTrackingEnabled,
20
- p as flushBufferedEvents,
21
- q as getCachedHref,
22
- r as getCachedHrefNoQuery,
23
- s as getFuncSpanHeader,
24
- t as getIdentifiedUser,
25
- u as getOrSetSessionId,
26
- v as getUrlAndStoredUuids,
27
- w as identify,
28
- x as initRecorder,
29
- z as initializeConsolePlugin,
30
- A as initializeDomContentEvents,
31
- B as initializeFunctionSpanTrackingFromApi,
32
- C as initializePerformancePlugin,
33
- E as initializeRecording,
34
- F as initializeWebSocket,
35
- G as invalidateUrlCache,
36
- H as isFunctionSpanTrackingEnabled,
37
- I as maskInputFn,
38
- J as matchUrlWithWildcard,
39
- K as nowTimestamp,
40
- L as onNavigationChange,
41
- M as openReportIssueModal,
42
- N as requestTimeSync,
43
- O as restoreFuncSpanState,
44
- P as sendDomainsToNotPropagateHeaderTo,
45
- Q as sendEvent,
46
- R as sendGraphQLRequest,
47
- T as sendMessage,
48
- U as startRecording,
49
- V as startRecordingSession,
50
- W as toAbsoluteUrl,
51
- X as trackingEvent,
52
- Y as withAppUrlMetadata
14
+ j as enableFunctionSpanTracking,
15
+ k as ensureHrefCache,
16
+ l as eventSize,
17
+ m as fetchAndSendIp,
18
+ n as fetchCaptureSettings,
19
+ o as fetchEngineeringTicketPlatformIntegrations,
20
+ p as fetchFunctionSpanTrackingEnabled,
21
+ q as flushBufferedEvents,
22
+ r as getBinaryDropCount,
23
+ s as getBufferedAmount,
24
+ t as getCachedHref,
25
+ v as getCachedHrefNoQuery,
26
+ w as getFuncSpanHeader,
27
+ x as getIdentifiedUser,
28
+ z as getOrSetSessionId,
29
+ A as getUrlAndStoredUuids,
30
+ B as identify,
31
+ C as initRecorder,
32
+ E as initializeConsolePlugin,
33
+ F as initializeDomContentEvents,
34
+ G as initializeFunctionSpanTrackingFromApi,
35
+ H as initializePerformancePlugin,
36
+ I as initializeRecording,
37
+ J as initializeWebSocket,
38
+ K as invalidateUrlCache,
39
+ L as isFunctionSpanTrackingEnabled,
40
+ M as isTransportSaturated,
41
+ N as maskCanvasDraws,
42
+ O as maskInputFn,
43
+ P as matchUrlWithWildcard,
44
+ Q as nowTimestamp,
45
+ R as onNavigationChange,
46
+ T as openReportIssueModal,
47
+ U as registerChart,
48
+ V as registerFabricCanvas,
49
+ W as registerKonvaStage,
50
+ X as registerPixiApp,
51
+ Y as registerThreeScene,
52
+ Z as requestTimeSync,
53
+ _ as restoreFuncSpanState,
54
+ $ as sendDomainsToNotPropagateHeaderTo,
55
+ a0 as sendEvent,
56
+ a1 as sendGraphQLRequest,
57
+ a2 as sendMessage,
58
+ a3 as startRecording,
59
+ a4 as startRecordingSession,
60
+ a5 as toAbsoluteUrl,
61
+ a6 as trackingEvent,
62
+ a7 as withAppUrlMetadata,
63
+ a8 as wsSendBinary
53
64
  };
Binary file
Binary file