@offscript/sdk 0.1.7 → 0.1.8

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.
@@ -1,4 +1,4 @@
1
- import { Offscript } from './chunk-LUTOFXSV.js';
1
+ import { Offscript } from './chunk-W27UMAEC.js';
2
2
 
3
3
  // src/adapters/vanilla.ts
4
4
  async function createOffscript(config) {
@@ -13,5 +13,5 @@ async function startOffscript(config) {
13
13
  }
14
14
 
15
15
  export { createOffscript, startOffscript };
16
- //# sourceMappingURL=chunk-ZB5N4KJE.js.map
17
- //# sourceMappingURL=chunk-ZB5N4KJE.js.map
16
+ //# sourceMappingURL=chunk-IOIVKJTV.js.map
17
+ //# sourceMappingURL=chunk-IOIVKJTV.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/vanilla.ts"],"names":[],"mappings":";;;AAIA,eAAsB,gBAAgB,MAAA,EAA6C;AACjF,EAAA,MAAM,GAAA,GAAM,IAAI,SAAA,CAAU,MAAM,CAAA;AAChC,EAAA,MAAM,IAAI,IAAA,EAAK;AACf,EAAA,OAAO,GAAA;AACT;AAEA,eAAsB,eAAe,MAAA,EAA6C;AAChF,EAAA,MAAM,GAAA,GAAM,MAAM,eAAA,CAAgB,MAAM,CAAA;AACxC,EAAA,MAAM,IAAI,cAAA,EAAe;AACzB,EAAA,OAAO,GAAA;AACT","file":"chunk-ZB5N4KJE.js","sourcesContent":["import { Offscript } from \"../core/OffscriptSDK\";\nimport type { OffscriptConfig } from \"../types/config\";\n\n/** Atalho para aplicações JS tradicionais, Blade e MPA: inicializa a captura local. */\nexport async function createOffscript(config: OffscriptConfig): Promise<Offscript> {\n const sdk = new Offscript(config);\n await sdk.init();\n return sdk;\n}\n\nexport async function startOffscript(config: OffscriptConfig): Promise<Offscript> {\n const sdk = await createOffscript(config);\n await sdk.startRecording();\n return sdk;\n}\n"]}
1
+ {"version":3,"sources":["../src/adapters/vanilla.ts"],"names":[],"mappings":";;;AAIA,eAAsB,gBAAgB,MAAA,EAA6C;AACjF,EAAA,MAAM,GAAA,GAAM,IAAI,SAAA,CAAU,MAAM,CAAA;AAChC,EAAA,MAAM,IAAI,IAAA,EAAK;AACf,EAAA,OAAO,GAAA;AACT;AAEA,eAAsB,eAAe,MAAA,EAA6C;AAChF,EAAA,MAAM,GAAA,GAAM,MAAM,eAAA,CAAgB,MAAM,CAAA;AACxC,EAAA,MAAM,IAAI,cAAA,EAAe;AACzB,EAAA,OAAO,GAAA;AACT","file":"chunk-IOIVKJTV.js","sourcesContent":["import { Offscript } from \"../core/OffscriptSDK\";\nimport type { OffscriptConfig } from \"../types/config\";\n\n/** Atalho para aplicações JS tradicionais, Blade e MPA: inicializa a captura local. */\nexport async function createOffscript(config: OffscriptConfig): Promise<Offscript> {\n const sdk = new Offscript(config);\n await sdk.init();\n return sdk;\n}\n\nexport async function startOffscript(config: OffscriptConfig): Promise<Offscript> {\n const sdk = await createOffscript(config);\n await sdk.startRecording();\n return sdk;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { buildPlaywrightContract, SDK_VERSION, SelectorGenerator, ACTION_SCHEMA_VERSION } from './chunk-4EPIPNDL.js';
1
+ import { buildPlaywrightContract, SDK_VERSION, SelectorGenerator, ACTION_SCHEMA_VERSION } from './chunk-AQG5MIW5.js';
2
2
 
3
3
  // src/types/config.ts
4
4
  var DEFAULT_RECORDING = {
@@ -1253,6 +1253,8 @@ var IframeRecorder = class {
1253
1253
  var PRIORITY = { 2: "critical", 4: "critical", 5: "critical", 0: "normal", 1: "normal", 3: "normal", 6: "normal" };
1254
1254
  var URL_ATTRS = ["src", "href", "action", "poster", "data-src", "data", "formaction", "srcset", "cite", "rr_src"];
1255
1255
  var LOW_SOURCES = /* @__PURE__ */ new Set([1, 6, 12, 3]);
1256
+ var SNAPSHOT_DEBOUNCE_MS = 200;
1257
+ var SNAPSHOT_SETTLE_TIMEOUT_MS = 5e3;
1256
1258
  var RRWebRecorder = class {
1257
1259
  constructor(sink) {
1258
1260
  this.sink = sink;
@@ -1261,6 +1263,7 @@ var RRWebRecorder = class {
1261
1263
  this.stopped = true;
1262
1264
  this.needsSnapshot = false;
1263
1265
  this.snapshotsTaken = 0;
1266
+ this.settling = false;
1264
1267
  }
1265
1268
  async start() {
1266
1269
  this.stopped = false;
@@ -1292,6 +1295,64 @@ var RRWebRecorder = class {
1292
1295
  // erros internos do rrweb não propagam
1293
1296
  };
1294
1297
  this.stopFn = mod.record(options) ?? void 0;
1298
+ this.scheduleSnapshot();
1299
+ }
1300
+ /** Captura o DOM depois do ciclo de renderização atual (Vue, React, etc.). */
1301
+ scheduleSnapshot() {
1302
+ this.cancelScheduledSnapshot();
1303
+ if (typeof window === "undefined") return;
1304
+ this.settling = true;
1305
+ if (typeof MutationObserver !== "undefined" && document.documentElement) {
1306
+ this.settleObserver = new MutationObserver(() => this.notifySnapshotActivity());
1307
+ this.settleObserver.observe(document.documentElement, {
1308
+ attributes: true,
1309
+ characterData: true,
1310
+ childList: true,
1311
+ subtree: true
1312
+ });
1313
+ }
1314
+ this.settleTimeout = window.setTimeout(() => this.captureSettledSnapshot(), SNAPSHOT_SETTLE_TIMEOUT_MS);
1315
+ }
1316
+ notifySnapshotActivity() {
1317
+ if (!this.settling || typeof window === "undefined") return;
1318
+ if (this.scheduledSnapshotTimer !== void 0) window.clearTimeout(this.scheduledSnapshotTimer);
1319
+ this.scheduledSnapshotTimer = window.setTimeout(() => this.captureSettledSnapshot(), SNAPSHOT_DEBOUNCE_MS);
1320
+ }
1321
+ captureSettledSnapshot() {
1322
+ if (!this.settling || typeof window === "undefined") return;
1323
+ this.settling = false;
1324
+ this.cancelScheduledSnapshot();
1325
+ const capture = () => {
1326
+ if (!this.stopped && !this.paused) this.takeFullSnapshot();
1327
+ };
1328
+ if (typeof window.requestAnimationFrame !== "function") {
1329
+ capture();
1330
+ return;
1331
+ }
1332
+ this.scheduledSnapshotFrame = window.requestAnimationFrame(() => {
1333
+ this.scheduledSnapshotFrame = window.requestAnimationFrame(() => {
1334
+ this.scheduledSnapshotFrame = void 0;
1335
+ capture();
1336
+ });
1337
+ });
1338
+ }
1339
+ cancelScheduledSnapshot() {
1340
+ if (typeof window === "undefined") return;
1341
+ this.settling = false;
1342
+ if (this.scheduledSnapshotFrame !== void 0) {
1343
+ window.cancelAnimationFrame(this.scheduledSnapshotFrame);
1344
+ this.scheduledSnapshotFrame = void 0;
1345
+ }
1346
+ if (this.scheduledSnapshotTimer !== void 0) {
1347
+ window.clearTimeout(this.scheduledSnapshotTimer);
1348
+ this.scheduledSnapshotTimer = void 0;
1349
+ }
1350
+ if (this.settleTimeout !== void 0) {
1351
+ window.clearTimeout(this.settleTimeout);
1352
+ this.settleTimeout = void 0;
1353
+ }
1354
+ this.settleObserver?.disconnect();
1355
+ this.settleObserver = void 0;
1295
1356
  }
1296
1357
  onEvent(event) {
1297
1358
  if (this.paused) return;
@@ -1299,8 +1360,11 @@ var RRWebRecorder = class {
1299
1360
  this.sanitizeEvent(event);
1300
1361
  let priority = PRIORITY[event.type] ?? "normal";
1301
1362
  if (event.type === 3 && LOW_SOURCES.has(event.data?.source)) priority = "low";
1302
- if (event.type === 2) this.snapshotsTaken++;
1303
1363
  const emitted = this.sink.emit("rrweb", event, { priority, at: event.timestamp });
1364
+ if (event.type === 2) {
1365
+ this.needsSnapshot = !emitted;
1366
+ if (emitted) this.snapshotsTaken++;
1367
+ }
1304
1368
  if (!emitted && event.type === 3) this.needsSnapshot = true;
1305
1369
  });
1306
1370
  }
@@ -1333,13 +1397,15 @@ var RRWebRecorder = class {
1333
1397
  /** Chamado pelo núcleo quando há folga novamente após descartes. */
1334
1398
  recoverIfNeeded() {
1335
1399
  if (this.needsSnapshot && !this.paused) {
1336
- this.needsSnapshot = false;
1337
1400
  this.takeFullSnapshot();
1338
1401
  }
1339
1402
  }
1340
1403
  takeFullSnapshot() {
1341
1404
  if (this.stopped) return;
1342
- guard(this.sink, this.name, () => this.mod?.record.takeFullSnapshot(true));
1405
+ guard(this.sink, this.name, () => {
1406
+ if (!this.mod?.record) return;
1407
+ this.mod.record.takeFullSnapshot(true);
1408
+ });
1343
1409
  }
1344
1410
  addCustomEvent(tag, payload) {
1345
1411
  if (this.stopped) return;
@@ -1350,6 +1416,7 @@ var RRWebRecorder = class {
1350
1416
  }
1351
1417
  pause() {
1352
1418
  this.paused = true;
1419
+ this.cancelScheduledSnapshot();
1353
1420
  }
1354
1421
  /** Ao retomar, as mutações perdidas durante a pausa exigem um snapshot completo. */
1355
1422
  resume() {
@@ -1357,7 +1424,9 @@ var RRWebRecorder = class {
1357
1424
  this.takeFullSnapshot();
1358
1425
  }
1359
1426
  stop() {
1427
+ if (!this.stopped && !this.paused) this.takeFullSnapshot();
1360
1428
  this.stopped = true;
1429
+ this.cancelScheduledSnapshot();
1361
1430
  safe(() => this.stopFn?.());
1362
1431
  this.stopFn = void 0;
1363
1432
  }
@@ -2213,7 +2282,7 @@ var SUCCESS_TEMPLATE = `
2213
2282
  var TOKEN_STORAGE_KEY = "offscript-session-token";
2214
2283
  var DRAFT_STORAGE_KEY = "offscript-draft-key";
2215
2284
  var COMPLETED_TOKEN_STORAGE_KEY = "offscript-completed-token";
2216
- var RECORDING_SEGMENT_START_QUERY = "offscript_recording_start";
2285
+ var NAVIGATION_SETTLE_WINDOW_MS = 5e3;
2217
2286
  function tokenFingerprint(token) {
2218
2287
  let first = 2166136261;
2219
2288
  let second = 2654435769;
@@ -2240,6 +2309,7 @@ var Offscript = class {
2240
2309
  this.pageId = uid("page_");
2241
2310
  this.submitted = false;
2242
2311
  this.launchTokenChanged = false;
2312
+ this.snapshotAfterNavigationUntil = 0;
2243
2313
  this.config = resolveConfig(config);
2244
2314
  this.sanitizer = new DataSanitizer(this.config.privacy);
2245
2315
  }
@@ -2255,11 +2325,19 @@ var Offscript = class {
2255
2325
  this.initPromise ?? (this.initPromise = this.initialize());
2256
2326
  await this.initPromise;
2257
2327
  if (this.restorePromise) await this.restorePromise;
2258
- if (this.config.controls && this.launchToken && !this.controls) {
2328
+ this.exposeRunnerBridge();
2329
+ if (this.config.controls && this.launchToken && !this.controls && !window.__offscriptRunner?.enabled) {
2259
2330
  this.controls = new RecordingControls(this);
2260
2331
  this.controls.mount();
2261
2332
  }
2262
2333
  }
2334
+ exposeRunnerBridge() {
2335
+ const bridge = window.__offscriptRunner;
2336
+ if (!bridge?.enabled || !bridge.endpoint) return;
2337
+ bridge.startRecording = () => this.startRecording();
2338
+ bridge.complete = () => this.complete({ endpoint: bridge.endpoint });
2339
+ bridge.ready = true;
2340
+ }
2263
2341
  async initialize() {
2264
2342
  this.resolveLaunchToken();
2265
2343
  if (this.launchToken && !this.config.persistenceKey) {
@@ -2315,27 +2393,8 @@ var Offscript = class {
2315
2393
  async initializeDraft() {
2316
2394
  if (!this.config.persistenceKey) return;
2317
2395
  this.store = await RecordingStore.open(this.config.persistenceKey);
2318
- const startsNewSegment = await this.consumeRecordingSegmentMarker();
2319
- if (this.launchTokenChanged || startsNewSegment) await this.store.clear();
2396
+ if (this.launchTokenChanged) await this.store.clear();
2320
2397
  await this.restoreDraft();
2321
- if (startsNewSegment) await this.startFreshRecording();
2322
- }
2323
- async consumeRecordingSegmentMarker() {
2324
- const url = new URL(window.location.href);
2325
- const marker = url.searchParams.get(RECORDING_SEGMENT_START_QUERY);
2326
- if (!marker) return false;
2327
- url.searchParams.delete(RECORDING_SEGMENT_START_QUERY);
2328
- window.history.replaceState(window.history.state, "", url.href);
2329
- if (!this.launchToken || !globalThis.crypto?.subtle) return false;
2330
- let fingerprint;
2331
- try {
2332
- const tokenBytes = new TextEncoder().encode(this.launchToken);
2333
- const digest = await globalThis.crypto.subtle.digest("SHA-256", tokenBytes);
2334
- fingerprint = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
2335
- } catch {
2336
- return false;
2337
- }
2338
- return marker === fingerprint;
2339
2398
  }
2340
2399
  async restoreDraft() {
2341
2400
  const saved = await this.store.load();
@@ -2549,6 +2608,7 @@ var Offscript = class {
2549
2608
  this.rrweb?.recoverIfNeeded();
2550
2609
  const sequence = ++this.sequence;
2551
2610
  const timestamp = opts.at ?? Date.now();
2611
+ if (kind === "http" && timestamp <= this.snapshotAfterNavigationUntil) this.rrweb?.notifySnapshotActivity();
2552
2612
  const id = `${this.pageId}:${sequence}`;
2553
2613
  let payload = data;
2554
2614
  if (kind === "action") {
@@ -2615,6 +2675,8 @@ var Offscript = class {
2615
2675
  this.actions?.recordNavigation(from, to, kind);
2616
2676
  this.emitLifecycle("navigation", { from: this.sanitizer.sanitizeUrl(from), to: this.sanitizer.sanitizeUrl(to), kind }, "critical");
2617
2677
  this.context?.record("navigation");
2678
+ this.snapshotAfterNavigationUntil = Date.now() + NAVIGATION_SETTLE_WINDOW_MS;
2679
+ this.rrweb?.scheduleSnapshot();
2618
2680
  }
2619
2681
  }));
2620
2682
  this.lifecycle.install();
@@ -2675,5 +2737,5 @@ var Offscript = class {
2675
2737
  Offscript.version = SDK_VERSION;
2676
2738
 
2677
2739
  export { ConfigError, DEFAULT_PRIVACY, DEFAULT_RECORDING, DataSanitizer, InvalidStateError, Offscript, OffscriptError };
2678
- //# sourceMappingURL=chunk-LUTOFXSV.js.map
2679
- //# sourceMappingURL=chunk-LUTOFXSV.js.map
2740
+ //# sourceMappingURL=chunk-W27UMAEC.js.map
2741
+ //# sourceMappingURL=chunk-W27UMAEC.js.map