browser-commander 0.27.0 → 0.28.0
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/CHANGELOG.md +16 -0
- package/README.md +4 -0
- package/eslint.config.js +11 -0
- package/package.json +5 -2
- package/src/browser/connector.js +117 -13
- package/src/browser/launch-options.js +2 -1
- package/src/browser/launch-restrictions.json +33 -2
- package/src/browser/launcher.js +26 -1
- package/src/browser/persistent-session.js +360 -0
- package/src/browser/profile-directory.js +20 -0
- package/src/browser/real-browser.js +7 -2
- package/src/browser/restrictions.js +13 -4
- package/src/browser/session-watchdog.js +71 -0
- package/src/browser/session-worker.js +70 -0
- package/src/browser/webdriver.js +4 -0
- package/src/capture/encoding.js +327 -0
- package/src/capture/errors.js +12 -0
- package/src/capture/files.js +9 -0
- package/src/capture/index.js +5 -0
- package/src/capture/recording.js +136 -0
- package/src/capture/screenshot.js +222 -0
- package/src/capture/video.js +186 -0
- package/src/capture/viewport.js +68 -0
- package/src/capture/worker.js +22 -0
- package/src/cli/args.js +66 -1
- package/src/cli/commands.js +113 -2
- package/src/cli/high-level-methods.js +40 -3
- package/src/cli/sessions.js +11 -3
- package/src/core/commander-features.js +105 -0
- package/src/core/constants.js +1 -0
- package/src/core/navigation-manager.js +4 -2
- package/src/core/navigation-readiness.js +30 -2
- package/src/core/navigation-safety.js +51 -0
- package/src/core/page-trigger-manager.js +115 -30
- package/src/core/readiness.js +10 -2
- package/src/elements/locators.js +26 -32
- package/src/elements/selectors.js +37 -81
- package/src/elements/visibility.js +32 -10
- package/src/factory.js +13 -12
- package/src/index.js +2 -0
- package/src/interactions/click.js +44 -0
- package/src/interactions/overlays.js +28 -0
- package/src/traces/bundle.js +14 -5
- package/src/traces/checkpoints.js +99 -0
- package/src/traces/links.js +117 -1
- package/src/traces/mutation-stream.js +1 -0
- package/src/traces/network.js +257 -0
- package/src/traces/page-capture.js +143 -52
- package/src/traces/reader.js +8 -10
- package/src/traces/recorder.js +221 -245
- package/src/traces/render.js +168 -0
- package/src/traces/rolling.js +180 -0
- package/src/traces/start-cleanup.js +15 -0
- package/src/traces/stop.js +159 -0
- package/src/traces/storage.js +43 -0
- package/src/utilities/subprocess.js +32 -0
- package/tests/e2e/cli.e2e.test.js +20 -1
- package/tests/e2e/click-readiness.e2e.test.js +1 -1
- package/tests/unit/browser/launch-building-blocks.test.js +5 -1
- package/tests/unit/browser/launcher.test.js +5 -1
- package/tests/unit/browser/persistent-session.test.js +59 -0
- package/tests/unit/browser/quiet-ui.test.js +16 -0
- package/tests/unit/browser/tab-selection.test.js +38 -0
- package/tests/unit/capture/capture.test.js +52 -0
- package/tests/unit/capture/cleanup.test.js +24 -0
- package/tests/unit/capture/viewport.test.js +168 -0
- package/tests/unit/cli/dispatcher.test.js +25 -0
- package/tests/unit/core/readiness.test.js +21 -0
- package/tests/unit/core/trigger-readiness.test.js +39 -0
- package/tests/unit/elements/visible-count.test.js +24 -0
- package/tests/unit/elements/wait-navigation.test.js +26 -0
- package/tests/unit/interactions/navigation-timeout.test.js +39 -0
- package/tests/unit/scripts/ci-timeout-budgets.test.js +1 -0
- package/tests/unit/traces/bundle.test.js +16 -0
- package/tests/unit/traces/network.test.js +97 -0
package/src/traces/recorder.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { abortTraceStart } from './start-cleanup.js';
|
|
2
|
+
import { createTraceStop } from './stop.js';
|
|
1
3
|
/**
|
|
2
4
|
* The trace recorder (issue #87).
|
|
3
5
|
*
|
|
@@ -7,28 +9,23 @@
|
|
|
7
9
|
* request, so every caller does not have to.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
|
-
import {
|
|
12
|
+
import { createCheckpointRecorder } from './checkpoints.js';
|
|
11
13
|
import { openTraceBundle } from './bundle.js';
|
|
12
14
|
import { openTraceLinks } from './links.js';
|
|
13
15
|
import { createTraceIdentity } from './identity.js';
|
|
14
16
|
import { withDeadline } from './deadline.js';
|
|
15
17
|
import { createMutationStream } from './mutation-stream.js';
|
|
16
18
|
import { attachTimelineObservers } from './observers.js';
|
|
19
|
+
import { attachNetwork, validateNetworkOptions } from './network.js';
|
|
20
|
+
import { startRecording } from '../capture/recording.js';
|
|
21
|
+
import { normalizePrivacyOptions, redactValue } from './redaction.js';
|
|
17
22
|
import {
|
|
18
|
-
normalizePrivacyOptions,
|
|
19
|
-
redactUrl,
|
|
20
|
-
redactValue,
|
|
21
|
-
REDACTED,
|
|
22
|
-
} from './redaction.js';
|
|
23
|
-
import {
|
|
24
|
-
createManifest,
|
|
25
23
|
DEFAULT_CAPTURE_TIMEOUT,
|
|
26
24
|
TRACE_CHECKPOINT_REASON,
|
|
27
25
|
TRACE_DROP_REASON,
|
|
28
26
|
TRACE_EVENT,
|
|
29
27
|
TRACE_EVENT_SOURCES,
|
|
30
28
|
TRACE_MODE,
|
|
31
|
-
TRACE_OUTCOME,
|
|
32
29
|
TRACE_SCHEMA_VERSION,
|
|
33
30
|
} from './schema.js';
|
|
34
31
|
|
|
@@ -92,27 +89,51 @@ function validateLinksOptions(links) {
|
|
|
92
89
|
* @param {Object} options - Recorder options, see `commander.startTrace()`
|
|
93
90
|
* @returns {Promise<Object>} The running trace
|
|
94
91
|
*/
|
|
92
|
+
function normalizeTraceOptions(options) {
|
|
93
|
+
return {
|
|
94
|
+
...options,
|
|
95
|
+
page: options.page ?? options.commander?.page,
|
|
96
|
+
mode: options.mode ?? TRACE_MODE.CHECKPOINTS,
|
|
97
|
+
screenshots: options.screenshots ?? 'checkpoints',
|
|
98
|
+
dom: options.dom ?? {},
|
|
99
|
+
privacy: options.privacy ?? {},
|
|
100
|
+
limits: options.limits ?? {},
|
|
101
|
+
links: options.links ?? null,
|
|
102
|
+
network: options.network ?? false,
|
|
103
|
+
strict: options.strict ?? false,
|
|
104
|
+
captureTimeoutMs: options.captureTimeoutMs ?? DEFAULT_CAPTURE_TIMEOUT,
|
|
105
|
+
commanderVersion: options.commanderVersion ?? null,
|
|
106
|
+
log: options.log ?? options.commander?.log,
|
|
107
|
+
now: options.now ?? (() => Date.now()),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
95
111
|
export async function startTrace(options = {}) {
|
|
112
|
+
if (options.limits?.rotate) {
|
|
113
|
+
const { startRollingTrace } = await import('./rolling.js');
|
|
114
|
+
return startRollingTrace(options, startTrace);
|
|
115
|
+
}
|
|
96
116
|
requireTraceSupport(options);
|
|
97
117
|
const {
|
|
98
118
|
commander,
|
|
99
|
-
page
|
|
119
|
+
page,
|
|
100
120
|
output,
|
|
101
|
-
mode
|
|
121
|
+
mode,
|
|
102
122
|
initialCheckpoint,
|
|
103
|
-
screenshots
|
|
104
|
-
dom
|
|
123
|
+
screenshots,
|
|
124
|
+
dom,
|
|
105
125
|
events,
|
|
106
|
-
privacy
|
|
107
|
-
limits
|
|
108
|
-
links
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
privacy,
|
|
127
|
+
limits,
|
|
128
|
+
links,
|
|
129
|
+
network,
|
|
130
|
+
strict,
|
|
131
|
+
captureTimeoutMs,
|
|
132
|
+
commanderVersion,
|
|
133
|
+
log,
|
|
134
|
+
now,
|
|
114
135
|
monotonic,
|
|
115
|
-
} = options;
|
|
136
|
+
} = normalizeTraceOptions(options);
|
|
116
137
|
|
|
117
138
|
if (!page) {
|
|
118
139
|
throw new Error('startTrace requires a page or a commander');
|
|
@@ -139,8 +160,6 @@ export async function startTrace(options = {}) {
|
|
|
139
160
|
// added to it uninvited. Passing a string names the base checkpoint.
|
|
140
161
|
const baseCheckpoint =
|
|
141
162
|
initialCheckpoint ?? recorderMode === TRACE_MODE.CONTINUOUS;
|
|
142
|
-
const baseCheckpointName =
|
|
143
|
-
typeof baseCheckpoint === 'string' ? baseCheckpoint : 'initial';
|
|
144
163
|
|
|
145
164
|
// The Links Notation export is a second view of the same records, written
|
|
146
165
|
// as they are recorded (issue #94), so a run that is killed leaves a
|
|
@@ -157,21 +176,27 @@ export async function startTrace(options = {}) {
|
|
|
157
176
|
});
|
|
158
177
|
const startedAt = new Date(now()).toISOString();
|
|
159
178
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
try {
|
|
180
|
+
if (links) {
|
|
181
|
+
linksSink = await openTraceLinks({
|
|
182
|
+
output: links.output,
|
|
183
|
+
include: links.include,
|
|
184
|
+
dom: links.dom,
|
|
185
|
+
bundlePath: bundle.root,
|
|
186
|
+
schemaVersion: TRACE_SCHEMA_VERSION,
|
|
187
|
+
mode: recorderMode,
|
|
188
|
+
engine,
|
|
189
|
+
startedAt,
|
|
190
|
+
commanderVersion,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
await bundle.abort();
|
|
195
|
+
throw error;
|
|
171
196
|
}
|
|
172
197
|
|
|
173
198
|
let stopped = false;
|
|
174
|
-
|
|
199
|
+
const captureState = { index: 0 };
|
|
175
200
|
const checkpoints = [];
|
|
176
201
|
|
|
177
202
|
const note = (message) => log?.debug?.(`[trace] ${message}`);
|
|
@@ -199,7 +224,7 @@ export async function startTrace(options = {}) {
|
|
|
199
224
|
async function evaluateInPage(fn, argument) {
|
|
200
225
|
// Both engines take `(fn, arg)`; the commander's own evaluate already
|
|
201
226
|
// normalizes the difference, and is used when it is available.
|
|
202
|
-
if (commander?.evaluate) {
|
|
227
|
+
if (commander?.evaluate && typeof page.evaluate !== 'function') {
|
|
203
228
|
return await commander.evaluate(fn, argument);
|
|
204
229
|
}
|
|
205
230
|
return await page.evaluate(fn, argument);
|
|
@@ -243,233 +268,184 @@ export async function startTrace(options = {}) {
|
|
|
243
268
|
}
|
|
244
269
|
}
|
|
245
270
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const index = ++checkpointIndex;
|
|
260
|
-
|
|
261
|
-
// Mutations are drained first so the batches belong to the interval that
|
|
262
|
-
// ended here, not to the one that starts now.
|
|
263
|
-
await mutations.drain(index - 1 > 0 ? index - 1 : 0);
|
|
264
|
-
|
|
265
|
-
let captured = null;
|
|
266
|
-
try {
|
|
267
|
-
captured = await withDeadline(
|
|
268
|
-
evaluateInPage(captureSnapshotInPage, {
|
|
269
|
-
redactSelectors: privacyOptions.redactSelectors,
|
|
270
|
-
redactAttributes: privacyOptions.redactAttributes,
|
|
271
|
-
redacted: REDACTED,
|
|
272
|
-
html: domOptions.html,
|
|
273
|
-
liveControlState: domOptions.liveControlState,
|
|
274
|
-
openShadowRoots: domOptions.openShadowRoots,
|
|
275
|
-
maxHtmlBytes: limits.maxHtmlBytes ?? 0,
|
|
276
|
-
}),
|
|
277
|
-
captureTimeoutMs,
|
|
278
|
-
'trace checkpoint capture'
|
|
279
|
-
);
|
|
280
|
-
} catch (error) {
|
|
281
|
-
await bundle.drop({
|
|
282
|
-
reason: /closed/i.test(error.message)
|
|
283
|
-
? TRACE_DROP_REASON.PAGE_CLOSED
|
|
284
|
-
: TRACE_DROP_REASON.CAPTURE_FAILED,
|
|
285
|
-
member: `checkpoints/${index}`,
|
|
286
|
-
detail: error.message,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const shot = await screenshot(reason);
|
|
291
|
-
const state = captured?.state
|
|
292
|
-
? redactValue(captured.state, privacyOptions)
|
|
293
|
-
: null;
|
|
294
|
-
|
|
295
|
-
const members = await bundle.writeCheckpoint({
|
|
296
|
-
index,
|
|
297
|
-
html: captured?.html ?? undefined,
|
|
298
|
-
state: state ? { ...state, name, actor, reason } : undefined,
|
|
299
|
-
screenshot: shot ?? undefined,
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
const entry = {
|
|
303
|
-
index,
|
|
304
|
-
name,
|
|
305
|
-
actor,
|
|
306
|
-
reason,
|
|
307
|
-
url: state ? redactUrl(state.url, privacyOptions) : null,
|
|
308
|
-
truncated: Boolean(captured?.truncated),
|
|
309
|
-
members,
|
|
310
|
-
};
|
|
311
|
-
checkpoints.push(entry);
|
|
312
|
-
await record(TRACE_EVENT.CHECKPOINT, entry);
|
|
313
|
-
|
|
314
|
-
// The init script covers every document created from here on; this covers
|
|
315
|
-
// a frame that was attached without one, which is cheap because installing
|
|
316
|
-
// over a recorder that is already observing does nothing.
|
|
317
|
-
await mutations.install();
|
|
318
|
-
|
|
319
|
-
return entry;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const detachers = attachTimelineObservers({
|
|
323
|
-
commander,
|
|
324
|
-
page,
|
|
325
|
-
eventSources,
|
|
271
|
+
const checkpoint = createCheckpointRecorder({
|
|
272
|
+
isStopped: () => Boolean(stopped),
|
|
273
|
+
captureState,
|
|
274
|
+
mutations,
|
|
275
|
+
evaluateInPage,
|
|
276
|
+
privacyOptions,
|
|
277
|
+
domOptions,
|
|
278
|
+
links,
|
|
279
|
+
limits,
|
|
280
|
+
captureTimeoutMs,
|
|
281
|
+
bundle,
|
|
282
|
+
screenshot,
|
|
283
|
+
checkpoints,
|
|
326
284
|
record,
|
|
327
|
-
note,
|
|
328
|
-
identity,
|
|
329
|
-
now,
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
// Registered before the first record, so a navigation that starts in the same
|
|
333
|
-
// tick as the trace does is still recorded from its first mutation.
|
|
334
|
-
const detachInitScript = await mutations.installPersistent();
|
|
335
|
-
if (detachInitScript) {
|
|
336
|
-
detachers.push(detachInitScript);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
await record(TRACE_EVENT.TRACE_START, {
|
|
340
|
-
mode: recorderMode,
|
|
341
|
-
engine,
|
|
342
|
-
dom: domOptions,
|
|
343
|
-
events: eventSources,
|
|
344
|
-
initialCheckpoint: Boolean(baseCheckpoint),
|
|
345
285
|
});
|
|
346
|
-
await mutations.install();
|
|
347
286
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
287
|
+
let detachers = [],
|
|
288
|
+
detachNetwork = async () => {},
|
|
289
|
+
video = null;
|
|
290
|
+
try {
|
|
291
|
+
detachers = attachTimelineObservers({
|
|
292
|
+
commander,
|
|
293
|
+
page,
|
|
294
|
+
eventSources,
|
|
295
|
+
record,
|
|
296
|
+
note,
|
|
297
|
+
identity,
|
|
298
|
+
now,
|
|
352
299
|
});
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
|
|
300
|
+
detachNetwork = attachNetwork({ page, network, record, note });
|
|
301
|
+
video = options.video
|
|
302
|
+
? await startRecording({
|
|
303
|
+
page,
|
|
304
|
+
engine,
|
|
305
|
+
...(options.video === true ? {} : options.video),
|
|
306
|
+
})
|
|
307
|
+
: null;
|
|
308
|
+
let navigationCapture = Promise.resolve();
|
|
309
|
+
if (options.checkpointOnNavigation) {
|
|
310
|
+
const captureNavigation = () => {
|
|
311
|
+
navigationCapture = navigationCapture
|
|
312
|
+
.then(() =>
|
|
313
|
+
stopped
|
|
314
|
+
? null
|
|
315
|
+
: checkpoint('navigation', {
|
|
316
|
+
reason: 'navigation',
|
|
317
|
+
actor: 'recorder',
|
|
318
|
+
})
|
|
319
|
+
)
|
|
320
|
+
.catch((error) => note(error.message));
|
|
321
|
+
};
|
|
322
|
+
page.on('load', captureNavigation);
|
|
323
|
+
detachers.push(() => page.off('load', captureNavigation));
|
|
364
324
|
}
|
|
365
|
-
const { discard = false, error = null } = stopOptions;
|
|
366
325
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
fatal: true,
|
|
373
|
-
});
|
|
326
|
+
// Registered before the first record, so a navigation that starts in the same
|
|
327
|
+
// tick as the trace does is still recorded from its first mutation.
|
|
328
|
+
const detachInitScript = await mutations.installPersistent();
|
|
329
|
+
if (detachInitScript) {
|
|
330
|
+
detachers.push(detachInitScript);
|
|
374
331
|
}
|
|
375
|
-
await record(TRACE_EVENT.TRACE_STOP, { discarded: discard });
|
|
376
|
-
stopped = true;
|
|
377
332
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
333
|
+
await record(TRACE_EVENT.TRACE_START, {
|
|
334
|
+
mode: recorderMode,
|
|
335
|
+
engine,
|
|
336
|
+
dom: domOptions,
|
|
337
|
+
events: eventSources,
|
|
338
|
+
initialCheckpoint: Boolean(baseCheckpoint),
|
|
339
|
+
});
|
|
340
|
+
await mutations.install();
|
|
381
341
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
342
|
+
if (baseCheckpoint) {
|
|
343
|
+
await checkpoint(
|
|
344
|
+
typeof baseCheckpoint === 'string' ? baseCheckpoint : 'initial',
|
|
345
|
+
{
|
|
346
|
+
actor: 'recorder',
|
|
347
|
+
reason: TRACE_CHECKPOINT_REASON.INITIAL,
|
|
348
|
+
}
|
|
349
|
+
);
|
|
388
350
|
}
|
|
389
351
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
},
|
|
408
|
-
privacy: {
|
|
409
|
-
redactSelectors: privacyOptions.redactSelectors,
|
|
410
|
-
redactAttributes: privacyOptions.redactAttributes,
|
|
411
|
-
redactQueryParams: privacyOptions.redactQueryParams,
|
|
412
|
-
hasCallback: Boolean(privacyOptions.redact),
|
|
413
|
-
},
|
|
414
|
-
limits,
|
|
415
|
-
})
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
// Closed after the manifest, because the closing link reports the outcome
|
|
419
|
-
// the manifest settled on, and the control diffs are read back out of the
|
|
420
|
-
// finished bundle rather than kept in memory for the length of a run.
|
|
421
|
-
if (linksSink) {
|
|
422
|
-
await linksSink.close({ manifest, bundlePath: bundle.root });
|
|
352
|
+
let drainTask;
|
|
353
|
+
if (domOptions.mutations) {
|
|
354
|
+
const timer = setInterval(() => {
|
|
355
|
+
if (!drainTask) {
|
|
356
|
+
drainTask = mutations
|
|
357
|
+
.drain(captureState.index)
|
|
358
|
+
.catch((error) => note(error.message))
|
|
359
|
+
.finally(() => {
|
|
360
|
+
drainTask = null;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}, 500);
|
|
364
|
+
timer.unref?.();
|
|
365
|
+
detachers.push(async () => {
|
|
366
|
+
clearInterval(timer);
|
|
367
|
+
await drainTask;
|
|
368
|
+
});
|
|
423
369
|
}
|
|
424
370
|
|
|
425
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Stop recording and write the manifest.
|
|
373
|
+
*
|
|
374
|
+
* @param {Object} [stopOptions] - `{outcome, discard, error}`
|
|
375
|
+
* @returns {Promise<Object>} `{path, manifest, checkpoints}`
|
|
376
|
+
*/
|
|
377
|
+
const stop = createTraceStop({
|
|
378
|
+
getStopped: () => stopped,
|
|
379
|
+
setStopped: (value) => {
|
|
380
|
+
stopped = value;
|
|
381
|
+
},
|
|
382
|
+
options,
|
|
383
|
+
detachers,
|
|
384
|
+
navigationCapture: () => navigationCapture,
|
|
385
|
+
video,
|
|
386
|
+
detachNetwork,
|
|
387
|
+
mutations,
|
|
388
|
+
captureState,
|
|
389
|
+
record,
|
|
390
|
+
note,
|
|
391
|
+
bundle,
|
|
392
|
+
recorderMode,
|
|
393
|
+
startedAt,
|
|
394
|
+
now,
|
|
395
|
+
commanderVersion,
|
|
396
|
+
engine,
|
|
397
|
+
domOptions,
|
|
398
|
+
eventSources,
|
|
399
|
+
privacyOptions,
|
|
400
|
+
limits,
|
|
401
|
+
linksSink,
|
|
402
|
+
network,
|
|
403
|
+
checkpoints,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
return {
|
|
426
407
|
path: bundle.root,
|
|
427
|
-
manifest,
|
|
428
|
-
checkpoints: [...checkpoints],
|
|
429
|
-
problems: [...bundle.problems, ...(linksSink?.problems ?? [])],
|
|
430
408
|
links: linksSink?.path ?? null,
|
|
409
|
+
mode: recorderMode,
|
|
410
|
+
get bytesWritten() {
|
|
411
|
+
return bundle.bytesWritten;
|
|
412
|
+
},
|
|
413
|
+
checkpoint,
|
|
414
|
+
/**
|
|
415
|
+
* Record an event a caller cares about on the same timeline.
|
|
416
|
+
*
|
|
417
|
+
* @param {string} name - What happened
|
|
418
|
+
* @param {Object} [data] - Details, redacted before writing
|
|
419
|
+
* @returns {Promise<Object|null>} The written event
|
|
420
|
+
*/
|
|
421
|
+
event: (name, data = {}) =>
|
|
422
|
+
record(TRACE_EVENT.INTERACTION, {
|
|
423
|
+
action: name,
|
|
424
|
+
actor: 'caller',
|
|
425
|
+
...data,
|
|
426
|
+
}),
|
|
427
|
+
stop,
|
|
428
|
+
get stopped() {
|
|
429
|
+
return Boolean(stopped);
|
|
430
|
+
},
|
|
431
|
+
get checkpoints() {
|
|
432
|
+
return [...checkpoints];
|
|
433
|
+
},
|
|
431
434
|
};
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return result;
|
|
435
|
+
} catch (error) {
|
|
436
|
+
await abortTraceStart({
|
|
437
|
+
detachers,
|
|
438
|
+
detachNetwork,
|
|
439
|
+
video,
|
|
440
|
+
mutations,
|
|
441
|
+
linksSink,
|
|
442
|
+
bundle,
|
|
443
|
+
});
|
|
444
|
+
throw error;
|
|
443
445
|
}
|
|
444
|
-
|
|
445
|
-
return {
|
|
446
|
-
path: bundle.root,
|
|
447
|
-
links: linksSink?.path ?? null,
|
|
448
|
-
mode: recorderMode,
|
|
449
|
-
checkpoint,
|
|
450
|
-
/**
|
|
451
|
-
* Record an event a caller cares about on the same timeline.
|
|
452
|
-
*
|
|
453
|
-
* @param {string} name - What happened
|
|
454
|
-
* @param {Object} [data] - Details, redacted before writing
|
|
455
|
-
* @returns {Promise<Object|null>} The written event
|
|
456
|
-
*/
|
|
457
|
-
event: (name, data = {}) =>
|
|
458
|
-
record(TRACE_EVENT.INTERACTION, {
|
|
459
|
-
action: name,
|
|
460
|
-
actor: 'caller',
|
|
461
|
-
...data,
|
|
462
|
-
}),
|
|
463
|
-
stop,
|
|
464
|
-
get stopped() {
|
|
465
|
-
return Boolean(stopped);
|
|
466
|
-
},
|
|
467
|
-
get checkpoints() {
|
|
468
|
-
return [...checkpoints];
|
|
469
|
-
},
|
|
470
|
-
};
|
|
471
446
|
}
|
|
472
447
|
|
|
473
448
|
function requireTraceSupport(options) {
|
|
449
|
+
validateNetworkOptions(options.network);
|
|
474
450
|
(options.page ?? options.commander?.page)?.requireFeature?.('tracing');
|
|
475
451
|
}
|