@offscript/sdk 0.1.5 → 0.1.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.
- package/README.md +10 -20
- package/dist/adapters/nuxt.cjs +3 -3
- package/dist/adapters/nuxt.js +2 -2
- package/dist/adapters/vanilla.cjs +5 -5
- package/dist/adapters/vanilla.js +3 -3
- package/dist/adapters/vue.cjs +3 -3
- package/dist/adapters/vue.js +2 -2
- package/dist/{chunk-XBXFGCUT.js → chunk-5ZBR6FI5.js} +88 -9
- package/dist/chunk-5ZBR6FI5.js.map +1 -0
- package/dist/{chunk-6ROP37WI.js → chunk-KHKIWCLM.js} +3 -3
- package/dist/{chunk-6ROP37WI.js.map → chunk-KHKIWCLM.js.map} +1 -1
- package/dist/{chunk-HPDLEAAL.cjs → chunk-MZXF2LF2.cjs} +3 -3
- package/dist/{chunk-HPDLEAAL.cjs.map → chunk-MZXF2LF2.cjs.map} +1 -1
- package/dist/{chunk-VYCDQLP7.cjs → chunk-PCJRHW66.cjs} +93 -14
- package/dist/chunk-PCJRHW66.cjs.map +1 -0
- package/dist/{chunk-V2YYWGIA.js → chunk-R2PPPR4K.js} +3 -3
- package/dist/{chunk-V2YYWGIA.js.map → chunk-R2PPPR4K.js.map} +1 -1
- package/dist/{chunk-2MY2FWIA.cjs → chunk-RAUSXUGS.cjs} +4 -4
- package/dist/{chunk-2MY2FWIA.cjs.map → chunk-RAUSXUGS.cjs.map} +1 -1
- package/dist/core/OffscriptSDK.d.ts +10 -2
- package/dist/index.cjs +20 -20
- package/dist/index.js +3 -3
- package/dist/playwright.cjs +13 -13
- package/dist/playwright.js +1 -1
- package/dist/types/config.d.ts +5 -2
- package/dist/types/events.d.ts +1 -1
- package/dist/types/recording.d.ts +2 -2
- package/docs/api-contract.md +1 -1
- package/docs/architecture.md +3 -3
- package/package.json +1 -1
- package/dist/chunk-VYCDQLP7.cjs.map +0 -1
- package/dist/chunk-XBXFGCUT.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
# @offscript/sdk
|
|
2
2
|
|
|
3
|
-
Biblioteca de captura no navegador: DOM com rrweb, ações, navegação SPA, HTTP, console, erros, contexto e iframes.
|
|
3
|
+
Biblioteca de captura no navegador: DOM com rrweb, ações, navegação SPA, HTTP, console, erros, contexto e iframes. O SDK lê `offscript_token` da query string, mantém o token e a gravação entre documentos da mesma origem e preserva a URL. O overlay só aparece quando há um token de execução.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { Offscript } from "@offscript/sdk";
|
|
7
7
|
|
|
8
8
|
const sdk = new Offscript({
|
|
9
|
+
endpoint: "https://painel.example/api/recordings",
|
|
9
10
|
recording: { rrweb: true, actions: true, http: true, errors: true, iframes: true },
|
|
10
11
|
});
|
|
12
|
+
await sdk.init();
|
|
11
13
|
await sdk.startRecording();
|
|
12
|
-
const result = await sdk.complete(
|
|
13
|
-
endpoint: "https://painel.example/api/recordings",
|
|
14
|
-
token: tokenForUpload,
|
|
15
|
-
});
|
|
14
|
+
const result = await sdk.complete();
|
|
16
15
|
```
|
|
17
16
|
|
|
18
|
-
`complete` para os recorders e faz um único POST JSON com `Authorization: Bearer <token>`. O
|
|
17
|
+
`complete()` para os recorders e faz um único POST JSON com `Authorization: Bearer <token>`. O host configura o endpoint; o SDK obtém o token da URL ou da sessão da aba. Também e possível passar `endpoint` e `token` explícitamente a `complete` para integrações personalizadas.
|
|
19
18
|
|
|
20
|
-
O payload contém `recording` (envelopes rrweb, ações, console, erros, contexto, iframes e ciclo de vida), `request` (envelopes HTTP) e `regression` (contrato Playwright com condições iniciais e passos). Não
|
|
19
|
+
O payload contém `recording` (envelopes rrweb, ações, console, erros, contexto, iframes e ciclo de vida), `request` (envelopes HTTP) e `regression` (contrato Playwright com condições iniciais e passos). Não ha transporte durante a captura nem reenvio automático. Chamadas concorrentes de `complete` compartilham o envio; após sucesso, chamadas repetidas retornam o resultado. Após falha, os dados continuam disponíveis para uma nova chamada explícita.
|
|
21
20
|
|
|
22
21
|
`stopRecording()` encerra localmente e devolve o payload sem publicar. `pauseRecording()` e `resumeRecording()` controlam a captura; retomar gera um snapshot completo. `discardRecording()` limpa os dados e volta ao estado inicial. `destroy()` libera a instrumentação sem enviar dados.
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
const sdk = new Offscript({
|
|
28
|
-
onComplete: () => sdk.complete({ endpoint: destination, token: uploadToken }),
|
|
29
|
-
});
|
|
30
|
-
await sdk.init();
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Sem callback, Finalizar apenas encerra localmente. `getState()` retorna `{ status, pending, lastError }`. Eventos públicos: `statusChange`, `error`, `recordingStart` e `recordingEnd` (captura encerrada, antes do upload).
|
|
23
|
+
Com `controls` habilitado (padrão), o overlay aparece em `init()` somente quando existe um token de execução na URL ou na sessão da aba. Iniciar grava localmente; Finalizar envia a gravação ao endpoint configurado usando o token do SDK. `onComplete` continua disponível para integrações que precisem substituir esse comportamento.
|
|
34
24
|
|
|
35
25
|
As opções `recording` e `privacy` estão tipadas em `src/types/config.ts`. `captureSensitiveValues` vem desabilitada; habilitá-la inclui valores sensíveis na captura. Configure máscaras, seletores bloqueados e URLs ignoradas conforme a aplicação.
|
|
36
26
|
|
|
@@ -40,8 +30,8 @@ Validação: `npm run typecheck`, `npm test`, `npm run build`, `npm run test:e2e
|
|
|
40
30
|
|
|
41
31
|
## Continuidade entre páginas
|
|
42
32
|
|
|
43
|
-
|
|
33
|
+
Em sessões iniciadas com token, o SDK cria e mantém automaticamente uma chave opaca de rascunho em `sessionStorage`. Com ela, `init()` restaura o rascunho e retoma a captura quando ela estava ativa, preserva a pausa ou disponibiliza a gravação encerrada para envio. `persistenceKey` continua opcional para integrações que queiram controlar essa chave.
|
|
44
34
|
|
|
45
|
-
Os eventos ficam no IndexedDB da origem. No descarregamento do documento, os últimos eventos ainda não confirmados são guardados em `sessionStorage` para a próxima
|
|
35
|
+
Os eventos ficam no IndexedDB da origem. No descarregamento do documento, os últimos eventos ainda não confirmados são guardados em `sessionStorage` para a próxima pagina. `complete()` limpa o rascunho após HTTP de sucesso; `discardRecording()` também o remove. `destroy()` fecha o armazenamento e libera a instrumentação, preservando o rascunho local.
|
|
46
36
|
|
|
47
|
-
O
|
|
37
|
+
O SDK lê o token de `offscript_token`, mantém a query na URL e guarda a credencial na sessão da aba para continuar após navegações. Após envio bem-sucedido, o SDK limpa o rascunho e não reabre o overlay nessa sessão; falhas mantém os dados para nova tentativa. A continuidade exige a mesma origem e o SDK inicializado em cada documento; armazenamento de navegador não é compartilhado entre domínios ou portas diferentes.
|
package/dist/adapters/nuxt.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-
|
|
3
|
+
var chunkPCJRHW66_cjs = require('../chunk-PCJRHW66.cjs');
|
|
4
|
+
require('../chunk-MZXF2LF2.cjs');
|
|
5
5
|
|
|
6
6
|
// src/adapters/nuxt.ts
|
|
7
7
|
function createNuxtOffscript(config) {
|
|
8
8
|
if (typeof window === "undefined") return null;
|
|
9
|
-
const sdk = new
|
|
9
|
+
const sdk = new chunkPCJRHW66_cjs.Offscript(config);
|
|
10
10
|
void sdk.init().catch(() => void 0);
|
|
11
11
|
return sdk;
|
|
12
12
|
}
|
package/dist/adapters/nuxt.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-
|
|
5
|
-
require('../chunk-
|
|
3
|
+
var chunkRAUSXUGS_cjs = require('../chunk-RAUSXUGS.cjs');
|
|
4
|
+
require('../chunk-PCJRHW66.cjs');
|
|
5
|
+
require('../chunk-MZXF2LF2.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "createOffscript", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunkRAUSXUGS_cjs.createOffscript; }
|
|
12
12
|
});
|
|
13
13
|
Object.defineProperty(exports, "startOffscript", {
|
|
14
14
|
enumerable: true,
|
|
15
|
-
get: function () { return
|
|
15
|
+
get: function () { return chunkRAUSXUGS_cjs.startOffscript; }
|
|
16
16
|
});
|
|
17
17
|
//# sourceMappingURL=vanilla.cjs.map
|
|
18
18
|
//# sourceMappingURL=vanilla.cjs.map
|
package/dist/adapters/vanilla.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { createOffscript, startOffscript } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
export { createOffscript, startOffscript } from '../chunk-R2PPPR4K.js';
|
|
2
|
+
import '../chunk-5ZBR6FI5.js';
|
|
3
|
+
import '../chunk-KHKIWCLM.js';
|
|
4
4
|
//# sourceMappingURL=vanilla.js.map
|
|
5
5
|
//# sourceMappingURL=vanilla.js.map
|
package/dist/adapters/vue.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('../chunk-
|
|
3
|
+
var chunkPCJRHW66_cjs = require('../chunk-PCJRHW66.cjs');
|
|
4
|
+
require('../chunk-MZXF2LF2.cjs');
|
|
5
5
|
|
|
6
6
|
// src/adapters/vue.ts
|
|
7
7
|
var OFFSCRIPT_KEY = "$offscript";
|
|
8
8
|
var OffscriptPlugin = {
|
|
9
9
|
install(app, config) {
|
|
10
|
-
const sdk = new
|
|
10
|
+
const sdk = new chunkPCJRHW66_cjs.Offscript(config);
|
|
11
11
|
void sdk.init().catch(() => void 0);
|
|
12
12
|
if (app.config?.globalProperties) {
|
|
13
13
|
app.config.globalProperties[OFFSCRIPT_KEY] = sdk;
|
package/dist/adapters/vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildPlaywrightContract, SDK_VERSION, SelectorGenerator, ACTION_SCHEMA_VERSION } from './chunk-
|
|
1
|
+
import { buildPlaywrightContract, SDK_VERSION, SelectorGenerator, ACTION_SCHEMA_VERSION } from './chunk-KHKIWCLM.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,19 @@ 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
|
+
function tokenFingerprint(token) {
|
|
2217
|
+
let first = 2166136261;
|
|
2218
|
+
let second = 2654435769;
|
|
2219
|
+
for (let index = 0; index < token.length; index++) {
|
|
2220
|
+
const character = token.charCodeAt(index);
|
|
2221
|
+
first = Math.imul(first ^ character, 16777619);
|
|
2222
|
+
second = Math.imul(second ^ character, 2246822519);
|
|
2223
|
+
}
|
|
2224
|
+
return `${first >>> 0}.${second >>> 0}`;
|
|
2225
|
+
}
|
|
2212
2226
|
var Offscript = class {
|
|
2213
2227
|
constructor(config = {}) {
|
|
2214
2228
|
this.bus = new Emitter();
|
|
@@ -2224,6 +2238,7 @@ var Offscript = class {
|
|
|
2224
2238
|
this.sequence = 0;
|
|
2225
2239
|
this.pageId = uid("page_");
|
|
2226
2240
|
this.submitted = false;
|
|
2241
|
+
this.launchTokenChanged = false;
|
|
2227
2242
|
this.config = resolveConfig(config);
|
|
2228
2243
|
this.sanitizer = new DataSanitizer(this.config.privacy);
|
|
2229
2244
|
}
|
|
@@ -2236,17 +2251,70 @@ var Offscript = class {
|
|
|
2236
2251
|
async init() {
|
|
2237
2252
|
this.assertAlive();
|
|
2238
2253
|
if (typeof window === "undefined") throw new OffscriptError("NOT_SUPPORTED", "O SDK requer um navegador");
|
|
2239
|
-
this.initPromise ?? (this.initPromise = this.
|
|
2254
|
+
this.initPromise ?? (this.initPromise = this.initialize());
|
|
2240
2255
|
await this.initPromise;
|
|
2241
2256
|
if (this.restorePromise) await this.restorePromise;
|
|
2242
|
-
if (this.config.controls && !this.controls) {
|
|
2257
|
+
if (this.config.controls && this.launchToken && !this.controls) {
|
|
2243
2258
|
this.controls = new RecordingControls(this);
|
|
2244
2259
|
this.controls.mount();
|
|
2245
2260
|
}
|
|
2246
2261
|
}
|
|
2262
|
+
async initialize() {
|
|
2263
|
+
this.resolveLaunchToken();
|
|
2264
|
+
if (this.launchToken && !this.config.persistenceKey) {
|
|
2265
|
+
this.config.persistenceKey = this.getOrCreateDraftKey();
|
|
2266
|
+
}
|
|
2267
|
+
await this.initializeDraft();
|
|
2268
|
+
}
|
|
2269
|
+
resolveLaunchToken() {
|
|
2270
|
+
const incomingToken = new URLSearchParams(window.location.search).get("offscript_token")?.trim();
|
|
2271
|
+
const previousToken = this.readSessionValue(TOKEN_STORAGE_KEY);
|
|
2272
|
+
const completedToken = this.readSessionValue(COMPLETED_TOKEN_STORAGE_KEY);
|
|
2273
|
+
const legacyCompletion = completedToken === "1" && Boolean(previousToken);
|
|
2274
|
+
const completedFingerprint = legacyCompletion && previousToken ? tokenFingerprint(previousToken) : completedToken;
|
|
2275
|
+
const candidateToken = incomingToken || previousToken || void 0;
|
|
2276
|
+
this.launchTokenChanged = Boolean(incomingToken && previousToken && incomingToken !== previousToken) || Boolean(incomingToken && completedFingerprint && completedFingerprint !== "1" && tokenFingerprint(incomingToken) !== completedFingerprint);
|
|
2277
|
+
if (this.launchTokenChanged) {
|
|
2278
|
+
this.removeSessionValue(COMPLETED_TOKEN_STORAGE_KEY);
|
|
2279
|
+
}
|
|
2280
|
+
if (legacyCompletion && previousToken && !this.launchTokenChanged) {
|
|
2281
|
+
this.writeSessionValue(COMPLETED_TOKEN_STORAGE_KEY, tokenFingerprint(previousToken));
|
|
2282
|
+
this.removeSessionValue(TOKEN_STORAGE_KEY);
|
|
2283
|
+
}
|
|
2284
|
+
const isCompleted = Boolean(candidateToken && completedFingerprint === tokenFingerprint(candidateToken));
|
|
2285
|
+
this.launchToken = isCompleted ? void 0 : candidateToken;
|
|
2286
|
+
if (incomingToken && !isCompleted) this.writeSessionValue(TOKEN_STORAGE_KEY, incomingToken);
|
|
2287
|
+
}
|
|
2288
|
+
getOrCreateDraftKey() {
|
|
2289
|
+
const saved = this.readSessionValue(DRAFT_STORAGE_KEY);
|
|
2290
|
+
if (saved) return saved;
|
|
2291
|
+
const key = uid("draft_");
|
|
2292
|
+
this.writeSessionValue(DRAFT_STORAGE_KEY, key);
|
|
2293
|
+
return key;
|
|
2294
|
+
}
|
|
2295
|
+
readSessionValue(key) {
|
|
2296
|
+
try {
|
|
2297
|
+
return window.sessionStorage.getItem(key);
|
|
2298
|
+
} catch {
|
|
2299
|
+
return null;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
writeSessionValue(key, value) {
|
|
2303
|
+
try {
|
|
2304
|
+
window.sessionStorage.setItem(key, value);
|
|
2305
|
+
} catch {
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
removeSessionValue(key) {
|
|
2309
|
+
try {
|
|
2310
|
+
window.sessionStorage.removeItem(key);
|
|
2311
|
+
} catch {
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2247
2314
|
async initializeDraft() {
|
|
2248
2315
|
if (!this.config.persistenceKey) return;
|
|
2249
2316
|
this.store = await RecordingStore.open(this.config.persistenceKey);
|
|
2317
|
+
if (this.launchTokenChanged) await this.store.clear();
|
|
2250
2318
|
await this.restoreDraft();
|
|
2251
2319
|
}
|
|
2252
2320
|
async restoreDraft() {
|
|
@@ -2356,7 +2424,7 @@ var Offscript = class {
|
|
|
2356
2424
|
this.bus.emit("recordingEnd", this.payload);
|
|
2357
2425
|
return this.payload;
|
|
2358
2426
|
}
|
|
2359
|
-
complete(options) {
|
|
2427
|
+
complete(options = {}) {
|
|
2360
2428
|
this.assertAlive();
|
|
2361
2429
|
if (this.completePromise) return this.completePromise;
|
|
2362
2430
|
if (this.submitted) return Promise.resolve(this.response);
|
|
@@ -2365,19 +2433,27 @@ var Offscript = class {
|
|
|
2365
2433
|
});
|
|
2366
2434
|
return this.completePromise;
|
|
2367
2435
|
}
|
|
2368
|
-
async doComplete(
|
|
2436
|
+
async doComplete(options) {
|
|
2437
|
+
const endpoint = options.endpoint ?? this.config.endpoint;
|
|
2438
|
+
const token = options.token?.trim() || this.launchToken;
|
|
2439
|
+
if (!endpoint) throw new ConfigError("Endpoint HTTP obrigat\xF3rio para envio");
|
|
2440
|
+
if (!token) throw new ConfigError("Token obrigat\xF3rio para envio");
|
|
2369
2441
|
const url = new URL(endpoint, location.href);
|
|
2370
2442
|
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
2443
|
const payload = await this.stopRecording();
|
|
2373
2444
|
try {
|
|
2374
2445
|
const response = await fetch(url.href, {
|
|
2375
2446
|
method: "POST",
|
|
2376
|
-
headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${token
|
|
2447
|
+
headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${token}` },
|
|
2377
2448
|
body: JSON.stringify(payload)
|
|
2378
2449
|
});
|
|
2379
2450
|
if (!response.ok) throw new OffscriptError("TRANSPORT_FAILED", `Falha ao enviar grava\xE7\xE3o: HTTP ${response.status}`, { status: response.status });
|
|
2380
2451
|
this.submitted = true;
|
|
2452
|
+
const completedToken = this.launchToken ?? token;
|
|
2453
|
+
this.writeSessionValue(COMPLETED_TOKEN_STORAGE_KEY, tokenFingerprint(completedToken));
|
|
2454
|
+
this.removeSessionValue(TOKEN_STORAGE_KEY);
|
|
2455
|
+
this.removeSessionValue(DRAFT_STORAGE_KEY);
|
|
2456
|
+
this.launchToken = void 0;
|
|
2381
2457
|
await this.store?.clear();
|
|
2382
2458
|
const text = await response.text();
|
|
2383
2459
|
try {
|
|
@@ -2387,6 +2463,8 @@ var Offscript = class {
|
|
|
2387
2463
|
}
|
|
2388
2464
|
this.lastError = void 0;
|
|
2389
2465
|
this.controls?.showSuccess();
|
|
2466
|
+
this.controls?.unmount();
|
|
2467
|
+
this.controls = void 0;
|
|
2390
2468
|
return this.response;
|
|
2391
2469
|
} catch (error) {
|
|
2392
2470
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2397,6 +2475,7 @@ var Offscript = class {
|
|
|
2397
2475
|
}
|
|
2398
2476
|
async finishRecording() {
|
|
2399
2477
|
if (this.config.onComplete) return this.config.onComplete();
|
|
2478
|
+
if (this.launchToken) return this.complete();
|
|
2400
2479
|
return this.stopRecording();
|
|
2401
2480
|
}
|
|
2402
2481
|
async discardRecording() {
|
|
@@ -2573,5 +2652,5 @@ var Offscript = class {
|
|
|
2573
2652
|
Offscript.version = SDK_VERSION;
|
|
2574
2653
|
|
|
2575
2654
|
export { ConfigError, DEFAULT_PRIVACY, DEFAULT_RECORDING, DataSanitizer, InvalidStateError, Offscript, OffscriptError };
|
|
2576
|
-
//# sourceMappingURL=chunk-
|
|
2577
|
-
//# sourceMappingURL=chunk-
|
|
2655
|
+
//# sourceMappingURL=chunk-5ZBR6FI5.js.map
|
|
2656
|
+
//# sourceMappingURL=chunk-5ZBR6FI5.js.map
|