@offscript/sdk 0.1.5 → 0.1.7

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 { buildPlaywrightContract, SDK_VERSION, SelectorGenerator, ACTION_SCHEMA_VERSION } from './chunk-6ROP37WI.js';
1
+ import { buildPlaywrightContract, SDK_VERSION, SelectorGenerator, ACTION_SCHEMA_VERSION } from './chunk-4EPIPNDL.js';
2
2
 
3
3
  // src/types/config.ts
4
4
  var DEFAULT_RECORDING = {
@@ -1531,6 +1531,7 @@ function resolveConfig(input) {
1531
1531
  privacy: resolvePrivacy(input.privacy),
1532
1532
  correlationWindowMs: input.correlationWindowMs ?? 3e3,
1533
1533
  controls: input.controls ?? true,
1534
+ endpoint: input.endpoint,
1534
1535
  persistenceKey: input.persistenceKey,
1535
1536
  onComplete: input.onComplete
1536
1537
  };
@@ -2209,6 +2210,20 @@ var SUCCESS_TEMPLATE = `
2209
2210
  `;
2210
2211
 
2211
2212
  // src/core/OffscriptSDK.ts
2213
+ var TOKEN_STORAGE_KEY = "offscript-session-token";
2214
+ var DRAFT_STORAGE_KEY = "offscript-draft-key";
2215
+ var COMPLETED_TOKEN_STORAGE_KEY = "offscript-completed-token";
2216
+ var RECORDING_SEGMENT_START_QUERY = "offscript_recording_start";
2217
+ function tokenFingerprint(token) {
2218
+ let first = 2166136261;
2219
+ let second = 2654435769;
2220
+ for (let index = 0; index < token.length; index++) {
2221
+ const character = token.charCodeAt(index);
2222
+ first = Math.imul(first ^ character, 16777619);
2223
+ second = Math.imul(second ^ character, 2246822519);
2224
+ }
2225
+ return `${first >>> 0}.${second >>> 0}`;
2226
+ }
2212
2227
  var Offscript = class {
2213
2228
  constructor(config = {}) {
2214
2229
  this.bus = new Emitter();
@@ -2224,6 +2239,7 @@ var Offscript = class {
2224
2239
  this.sequence = 0;
2225
2240
  this.pageId = uid("page_");
2226
2241
  this.submitted = false;
2242
+ this.launchTokenChanged = false;
2227
2243
  this.config = resolveConfig(config);
2228
2244
  this.sanitizer = new DataSanitizer(this.config.privacy);
2229
2245
  }
@@ -2236,18 +2252,90 @@ var Offscript = class {
2236
2252
  async init() {
2237
2253
  this.assertAlive();
2238
2254
  if (typeof window === "undefined") throw new OffscriptError("NOT_SUPPORTED", "O SDK requer um navegador");
2239
- this.initPromise ?? (this.initPromise = this.initializeDraft());
2255
+ this.initPromise ?? (this.initPromise = this.initialize());
2240
2256
  await this.initPromise;
2241
2257
  if (this.restorePromise) await this.restorePromise;
2242
- if (this.config.controls && !this.controls) {
2258
+ if (this.config.controls && this.launchToken && !this.controls) {
2243
2259
  this.controls = new RecordingControls(this);
2244
2260
  this.controls.mount();
2245
2261
  }
2246
2262
  }
2263
+ async initialize() {
2264
+ this.resolveLaunchToken();
2265
+ if (this.launchToken && !this.config.persistenceKey) {
2266
+ this.config.persistenceKey = this.getOrCreateDraftKey();
2267
+ }
2268
+ await this.initializeDraft();
2269
+ }
2270
+ resolveLaunchToken() {
2271
+ const incomingToken = new URLSearchParams(window.location.search).get("offscript_token")?.trim();
2272
+ const previousToken = this.readSessionValue(TOKEN_STORAGE_KEY);
2273
+ const completedToken = this.readSessionValue(COMPLETED_TOKEN_STORAGE_KEY);
2274
+ const legacyCompletion = completedToken === "1" && Boolean(previousToken);
2275
+ const completedFingerprint = legacyCompletion && previousToken ? tokenFingerprint(previousToken) : completedToken;
2276
+ const candidateToken = incomingToken || previousToken || void 0;
2277
+ this.launchTokenChanged = Boolean(incomingToken && previousToken && incomingToken !== previousToken) || Boolean(incomingToken && completedFingerprint && completedFingerprint !== "1" && tokenFingerprint(incomingToken) !== completedFingerprint);
2278
+ if (this.launchTokenChanged) {
2279
+ this.removeSessionValue(COMPLETED_TOKEN_STORAGE_KEY);
2280
+ }
2281
+ if (legacyCompletion && previousToken && !this.launchTokenChanged) {
2282
+ this.writeSessionValue(COMPLETED_TOKEN_STORAGE_KEY, tokenFingerprint(previousToken));
2283
+ this.removeSessionValue(TOKEN_STORAGE_KEY);
2284
+ }
2285
+ const isCompleted = Boolean(candidateToken && completedFingerprint === tokenFingerprint(candidateToken));
2286
+ this.launchToken = isCompleted ? void 0 : candidateToken;
2287
+ if (incomingToken && !isCompleted) this.writeSessionValue(TOKEN_STORAGE_KEY, incomingToken);
2288
+ }
2289
+ getOrCreateDraftKey() {
2290
+ const saved = this.readSessionValue(DRAFT_STORAGE_KEY);
2291
+ if (saved) return saved;
2292
+ const key = uid("draft_");
2293
+ this.writeSessionValue(DRAFT_STORAGE_KEY, key);
2294
+ return key;
2295
+ }
2296
+ readSessionValue(key) {
2297
+ try {
2298
+ return window.sessionStorage.getItem(key);
2299
+ } catch {
2300
+ return null;
2301
+ }
2302
+ }
2303
+ writeSessionValue(key, value) {
2304
+ try {
2305
+ window.sessionStorage.setItem(key, value);
2306
+ } catch {
2307
+ }
2308
+ }
2309
+ removeSessionValue(key) {
2310
+ try {
2311
+ window.sessionStorage.removeItem(key);
2312
+ } catch {
2313
+ }
2314
+ }
2247
2315
  async initializeDraft() {
2248
2316
  if (!this.config.persistenceKey) return;
2249
2317
  this.store = await RecordingStore.open(this.config.persistenceKey);
2318
+ const startsNewSegment = await this.consumeRecordingSegmentMarker();
2319
+ if (this.launchTokenChanged || startsNewSegment) await this.store.clear();
2250
2320
  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;
2251
2339
  }
2252
2340
  async restoreDraft() {
2253
2341
  const saved = await this.store.load();
@@ -2287,6 +2375,9 @@ var Offscript = class {
2287
2375
  }
2288
2376
  async doStart() {
2289
2377
  await this.init();
2378
+ await this.startFreshRecording();
2379
+ }
2380
+ async startFreshRecording() {
2290
2381
  if (this.status === "recording" || this.status === "paused") return;
2291
2382
  if (this.completePromise || this.stopPromise) throw new InvalidStateError("Finaliza\xE7\xE3o em andamento");
2292
2383
  await this.store?.clear();
@@ -2356,7 +2447,7 @@ var Offscript = class {
2356
2447
  this.bus.emit("recordingEnd", this.payload);
2357
2448
  return this.payload;
2358
2449
  }
2359
- complete(options) {
2450
+ complete(options = {}) {
2360
2451
  this.assertAlive();
2361
2452
  if (this.completePromise) return this.completePromise;
2362
2453
  if (this.submitted) return Promise.resolve(this.response);
@@ -2365,19 +2456,27 @@ var Offscript = class {
2365
2456
  });
2366
2457
  return this.completePromise;
2367
2458
  }
2368
- async doComplete({ endpoint, token }) {
2459
+ async doComplete(options) {
2460
+ const endpoint = options.endpoint ?? this.config.endpoint;
2461
+ const token = options.token?.trim() || this.launchToken;
2462
+ if (!endpoint) throw new ConfigError("Endpoint HTTP obrigat\xF3rio para envio");
2463
+ if (!token) throw new ConfigError("Token obrigat\xF3rio para envio");
2369
2464
  const url = new URL(endpoint, location.href);
2370
2465
  if (!["https:", "http:"].includes(url.protocol)) throw new ConfigError("Endpoint HTTP inv\xE1lido");
2371
- if (!token?.trim()) throw new ConfigError("Token obrigat\xF3rio para envio");
2372
2466
  const payload = await this.stopRecording();
2373
2467
  try {
2374
2468
  const response = await fetch(url.href, {
2375
2469
  method: "POST",
2376
- headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${token.trim()}` },
2470
+ headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${token}` },
2377
2471
  body: JSON.stringify(payload)
2378
2472
  });
2379
2473
  if (!response.ok) throw new OffscriptError("TRANSPORT_FAILED", `Falha ao enviar grava\xE7\xE3o: HTTP ${response.status}`, { status: response.status });
2380
2474
  this.submitted = true;
2475
+ const completedToken = this.launchToken ?? token;
2476
+ this.writeSessionValue(COMPLETED_TOKEN_STORAGE_KEY, tokenFingerprint(completedToken));
2477
+ this.removeSessionValue(TOKEN_STORAGE_KEY);
2478
+ this.removeSessionValue(DRAFT_STORAGE_KEY);
2479
+ this.launchToken = void 0;
2381
2480
  await this.store?.clear();
2382
2481
  const text = await response.text();
2383
2482
  try {
@@ -2387,6 +2486,8 @@ var Offscript = class {
2387
2486
  }
2388
2487
  this.lastError = void 0;
2389
2488
  this.controls?.showSuccess();
2489
+ this.controls?.unmount();
2490
+ this.controls = void 0;
2390
2491
  return this.response;
2391
2492
  } catch (error) {
2392
2493
  const err = error instanceof Error ? error : new Error(String(error));
@@ -2397,6 +2498,7 @@ var Offscript = class {
2397
2498
  }
2398
2499
  async finishRecording() {
2399
2500
  if (this.config.onComplete) return this.config.onComplete();
2501
+ if (this.launchToken) return this.complete();
2400
2502
  return this.stopRecording();
2401
2503
  }
2402
2504
  async discardRecording() {
@@ -2573,5 +2675,5 @@ var Offscript = class {
2573
2675
  Offscript.version = SDK_VERSION;
2574
2676
 
2575
2677
  export { ConfigError, DEFAULT_PRIVACY, DEFAULT_RECORDING, DataSanitizer, InvalidStateError, Offscript, OffscriptError };
2576
- //# sourceMappingURL=chunk-XBXFGCUT.js.map
2577
- //# sourceMappingURL=chunk-XBXFGCUT.js.map
2678
+ //# sourceMappingURL=chunk-LUTOFXSV.js.map
2679
+ //# sourceMappingURL=chunk-LUTOFXSV.js.map