@crouton-kit/capture 1.4.22 → 1.4.23
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 +2 -0
- package/bin/capture +605 -573
- package/package.json +1 -1
- package/src/capture.ts +1 -1
- package/src/cdp/args.ts +47 -1
package/bin/capture
CHANGED
|
@@ -9095,6 +9095,347 @@ var init_console_recorder = __esm({
|
|
|
9095
9095
|
}
|
|
9096
9096
|
});
|
|
9097
9097
|
|
|
9098
|
+
// src/output/artifact.ts
|
|
9099
|
+
function isUrlRef(ref) {
|
|
9100
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ref);
|
|
9101
|
+
}
|
|
9102
|
+
function stripTrailingSep(p) {
|
|
9103
|
+
return p.length > 1 && (p.endsWith("/") || p.endsWith(path5.sep)) ? p.slice(0, -1) : p;
|
|
9104
|
+
}
|
|
9105
|
+
function resolveRefFromPath(ref, config3) {
|
|
9106
|
+
const resolved = assertUnderCaptureRoot(stripTrailingSep(ref));
|
|
9107
|
+
const metaPath = path5.join(resolved, "meta.json");
|
|
9108
|
+
if (!fs5.existsSync(metaPath)) {
|
|
9109
|
+
throw new ArtifactResolutionError(
|
|
9110
|
+
ref,
|
|
9111
|
+
[resolved],
|
|
9112
|
+
`no ${config3.label} found at this path (missing meta.json)`,
|
|
9113
|
+
config3.creatingCommand
|
|
9114
|
+
);
|
|
9115
|
+
}
|
|
9116
|
+
return { kind: config3.kind, id: path5.basename(resolved), dir: resolved };
|
|
9117
|
+
}
|
|
9118
|
+
function resolveRefFromId(ref, config3) {
|
|
9119
|
+
const active = getActiveSession();
|
|
9120
|
+
if (!active) {
|
|
9121
|
+
throw new ArtifactResolutionError(
|
|
9122
|
+
ref,
|
|
9123
|
+
[],
|
|
9124
|
+
`no active capture session \u2014 a bare id resolves against the active session's ${config3.idSegments.join("/")} directory`,
|
|
9125
|
+
`capture session start (then re-run), or pass the absolute ${config3.label} path printed by ${config3.creatingCommand}`
|
|
9126
|
+
);
|
|
9127
|
+
}
|
|
9128
|
+
const dir = path5.join(active.dir, ...config3.idSegments, ref);
|
|
9129
|
+
const resolved = assertUnderCaptureRoot(dir);
|
|
9130
|
+
const metaPath = path5.join(resolved, "meta.json");
|
|
9131
|
+
if (!fs5.existsSync(metaPath)) {
|
|
9132
|
+
throw new ArtifactResolutionError(
|
|
9133
|
+
ref,
|
|
9134
|
+
[resolved],
|
|
9135
|
+
`no ${config3.label} named ${JSON.stringify(ref)} found in the active session (${active.sessionId})`,
|
|
9136
|
+
config3.creatingCommand
|
|
9137
|
+
);
|
|
9138
|
+
}
|
|
9139
|
+
return { kind: config3.kind, id: ref, dir: resolved };
|
|
9140
|
+
}
|
|
9141
|
+
function rejectRelativePath(ref) {
|
|
9142
|
+
if (!path5.isAbsolute(ref) && (ref.includes("/") || ref.includes(path5.sep))) {
|
|
9143
|
+
throw new ArtifactResolutionError(
|
|
9144
|
+
ref,
|
|
9145
|
+
[],
|
|
9146
|
+
"relative paths are not accepted \u2014 pass an absolute artifact path (as printed by the capturing command) or a bare id"
|
|
9147
|
+
);
|
|
9148
|
+
}
|
|
9149
|
+
}
|
|
9150
|
+
async function resolveSnapRef(ref, options = {}) {
|
|
9151
|
+
if (isUrlRef(ref)) {
|
|
9152
|
+
if (!options.onUrl) {
|
|
9153
|
+
throw new ArtifactResolutionError(ref, [], "this command does not accept a URL target \u2014 pass a snapshot id or path instead");
|
|
9154
|
+
}
|
|
9155
|
+
const result = await options.onUrl(ref);
|
|
9156
|
+
return { kind: "snap", id: result.id, dir: assertUnderCaptureRoot(result.dir) };
|
|
9157
|
+
}
|
|
9158
|
+
if (path5.isAbsolute(ref)) return resolveRefFromPath(ref, SNAP_CONFIG);
|
|
9159
|
+
rejectRelativePath(ref);
|
|
9160
|
+
return resolveRefFromId(ref, SNAP_CONFIG);
|
|
9161
|
+
}
|
|
9162
|
+
function resolveRecRef(ref) {
|
|
9163
|
+
if (isUrlRef(ref)) {
|
|
9164
|
+
throw new ArtifactResolutionError(
|
|
9165
|
+
ref,
|
|
9166
|
+
[],
|
|
9167
|
+
"a recording ref cannot be a URL \u2014 record one first with `capture motion rec <url> --do <action>` or `--start`/`--stop`"
|
|
9168
|
+
);
|
|
9169
|
+
}
|
|
9170
|
+
if (path5.isAbsolute(ref)) return resolveRefFromPath(ref, REC_CONFIG);
|
|
9171
|
+
rejectRelativePath(ref);
|
|
9172
|
+
return resolveRefFromId(ref, REC_CONFIG);
|
|
9173
|
+
}
|
|
9174
|
+
function resolveTraceRef(ref) {
|
|
9175
|
+
if (isUrlRef(ref)) {
|
|
9176
|
+
throw new ArtifactResolutionError(ref, [], "a trace ref cannot be a URL \u2014 record one first with `capture perf trace <url>` or `--start`/`--stop`");
|
|
9177
|
+
}
|
|
9178
|
+
const resolved = path5.isAbsolute(ref) ? resolveRefFromPath(ref, TRACE_CONFIG) : (rejectRelativePath(ref), resolveRefFromId(ref, TRACE_CONFIG));
|
|
9179
|
+
let meta;
|
|
9180
|
+
try {
|
|
9181
|
+
meta = JSON.parse(readPrivateFile(path5.join(resolved.dir, "meta.json")).toString("utf8"));
|
|
9182
|
+
} catch (error4) {
|
|
9183
|
+
throw new ArtifactResolutionError(ref, [path5.join(resolved.dir, "meta.json")], `could not read trace metadata: ${error4 instanceof Error ? error4.message : String(error4)}`, TRACE_CONFIG.creatingCommand);
|
|
9184
|
+
}
|
|
9185
|
+
const completion = meta.completion;
|
|
9186
|
+
if (completion !== "complete" && completion !== "partial" && completion !== "orphaned") {
|
|
9187
|
+
throw new ArtifactResolutionError(ref, [path5.join(resolved.dir, "meta.json")], "trace metadata has no valid completion state", TRACE_CONFIG.creatingCommand);
|
|
9188
|
+
}
|
|
9189
|
+
const inventory = meta.files;
|
|
9190
|
+
const inventoryMalformed = !Array.isArray(inventory);
|
|
9191
|
+
const inventoryMismatch = !inventoryMalformed && inventory.some((entry) => {
|
|
9192
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
|
|
9193
|
+
const item = entry;
|
|
9194
|
+
const name = item.name;
|
|
9195
|
+
const bytes = item.bytes;
|
|
9196
|
+
if (typeof name !== "string" || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0 || name.includes(path5.sep)) return true;
|
|
9197
|
+
try {
|
|
9198
|
+
return fs5.statSync(path5.join(resolved.dir, name)).size !== bytes;
|
|
9199
|
+
} catch {
|
|
9200
|
+
return true;
|
|
9201
|
+
}
|
|
9202
|
+
});
|
|
9203
|
+
const truncatedReason = inventoryMalformed ? "artifact_inventory_missing_or_malformed" : inventoryMismatch ? "artifact_inventory_mismatch" : void 0;
|
|
9204
|
+
return { ...resolved, completion: truncatedReason ? "truncated" : completion, ...truncatedReason ? { reason: truncatedReason } : typeof meta.reason === "string" ? { reason: meta.reason } : {} };
|
|
9205
|
+
}
|
|
9206
|
+
function creatingCommandFor(filename) {
|
|
9207
|
+
return CREATING_COMMAND_BY_FILE[filename];
|
|
9208
|
+
}
|
|
9209
|
+
function artifactPath(ref, filename, opts = {}) {
|
|
9210
|
+
const filePath = assertUnderCaptureRoot(path5.join(ref.dir, filename));
|
|
9211
|
+
const mustExist = opts.mustExist ?? true;
|
|
9212
|
+
if (mustExist && !fs5.existsSync(filePath)) {
|
|
9213
|
+
throw new ArtifactResolutionError(
|
|
9214
|
+
ref.id,
|
|
9215
|
+
[filePath],
|
|
9216
|
+
`${filename} is not present in ${ref.kind === "snap" ? "snapshot" : ref.kind === "rec" ? "recording" : "trace"} ${ref.id}`,
|
|
9217
|
+
creatingCommandFor(filename)
|
|
9218
|
+
);
|
|
9219
|
+
}
|
|
9220
|
+
return filePath;
|
|
9221
|
+
}
|
|
9222
|
+
function artifactExists(ref, filename) {
|
|
9223
|
+
return fs5.existsSync(assertUnderCaptureRoot(path5.join(ref.dir, filename)));
|
|
9224
|
+
}
|
|
9225
|
+
function readJsonArtifactFile(ref, filename) {
|
|
9226
|
+
const filePath = artifactPath(ref, filename);
|
|
9227
|
+
let raw;
|
|
9228
|
+
try {
|
|
9229
|
+
raw = fs5.readFileSync(filePath, "utf-8");
|
|
9230
|
+
} catch (err) {
|
|
9231
|
+
throw new ArtifactResolutionError(
|
|
9232
|
+
ref.id,
|
|
9233
|
+
[filePath],
|
|
9234
|
+
`could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
|
|
9235
|
+
creatingCommandFor(filename)
|
|
9236
|
+
);
|
|
9237
|
+
}
|
|
9238
|
+
try {
|
|
9239
|
+
return JSON.parse(raw);
|
|
9240
|
+
} catch (err) {
|
|
9241
|
+
throw new ArtifactResolutionError(
|
|
9242
|
+
ref.id,
|
|
9243
|
+
[filePath],
|
|
9244
|
+
`${filename} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
9245
|
+
creatingCommandFor(filename)
|
|
9246
|
+
);
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
function readNdjsonArtifactFile(ref, filename) {
|
|
9250
|
+
const filePath = artifactPath(ref, filename);
|
|
9251
|
+
let raw;
|
|
9252
|
+
try {
|
|
9253
|
+
raw = fs5.readFileSync(filePath, "utf-8");
|
|
9254
|
+
} catch (err) {
|
|
9255
|
+
throw new ArtifactResolutionError(
|
|
9256
|
+
ref.id,
|
|
9257
|
+
[filePath],
|
|
9258
|
+
`could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
|
|
9259
|
+
creatingCommandFor(filename)
|
|
9260
|
+
);
|
|
9261
|
+
}
|
|
9262
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
9263
|
+
return lines.map((line4, i) => {
|
|
9264
|
+
try {
|
|
9265
|
+
return JSON.parse(line4);
|
|
9266
|
+
} catch (err) {
|
|
9267
|
+
throw new ArtifactResolutionError(
|
|
9268
|
+
ref.id,
|
|
9269
|
+
[filePath],
|
|
9270
|
+
`${filename} line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
9271
|
+
creatingCommandFor(filename)
|
|
9272
|
+
);
|
|
9273
|
+
}
|
|
9274
|
+
});
|
|
9275
|
+
}
|
|
9276
|
+
function assertKind(ref, kind, fnName) {
|
|
9277
|
+
if (ref.kind !== kind) {
|
|
9278
|
+
throw new TypeError(`${fnName} expects a ${kind} ref, got a ${ref.kind} ref (${ref.id})`);
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
9281
|
+
function readMeta(ref) {
|
|
9282
|
+
return readJsonArtifactFile(ref, "meta.json");
|
|
9283
|
+
}
|
|
9284
|
+
function readGeometry(ref) {
|
|
9285
|
+
assertKind(ref, "snap", "readGeometry");
|
|
9286
|
+
return readJsonArtifactFile(ref, "geometry.json");
|
|
9287
|
+
}
|
|
9288
|
+
function readStyles(ref) {
|
|
9289
|
+
assertKind(ref, "snap", "readStyles");
|
|
9290
|
+
return readJsonArtifactFile(ref, "styles.json");
|
|
9291
|
+
}
|
|
9292
|
+
function readHittest(ref) {
|
|
9293
|
+
assertKind(ref, "snap", "readHittest");
|
|
9294
|
+
return readJsonArtifactFile(ref, "hittest.json");
|
|
9295
|
+
}
|
|
9296
|
+
function readText(ref) {
|
|
9297
|
+
assertKind(ref, "snap", "readText");
|
|
9298
|
+
return readJsonArtifactFile(ref, "text.json");
|
|
9299
|
+
}
|
|
9300
|
+
function readForms(ref) {
|
|
9301
|
+
assertKind(ref, "snap", "readForms");
|
|
9302
|
+
return readJsonArtifactFile(ref, "forms.json");
|
|
9303
|
+
}
|
|
9304
|
+
function readAnimation(ref) {
|
|
9305
|
+
assertKind(ref, "snap", "readAnimation");
|
|
9306
|
+
return readJsonArtifactFile(ref, "animation.json");
|
|
9307
|
+
}
|
|
9308
|
+
function readAx(ref) {
|
|
9309
|
+
assertKind(ref, "snap", "readAx");
|
|
9310
|
+
return readJsonArtifactFile(ref, "ax.json");
|
|
9311
|
+
}
|
|
9312
|
+
function readQueries(ref) {
|
|
9313
|
+
assertKind(ref, "snap", "readQueries");
|
|
9314
|
+
return readJsonArtifactFile(ref, "queries.json");
|
|
9315
|
+
}
|
|
9316
|
+
function readFocus(ref) {
|
|
9317
|
+
assertKind(ref, "snap", "readFocus");
|
|
9318
|
+
return readJsonArtifactFile(ref, "focus.json");
|
|
9319
|
+
}
|
|
9320
|
+
function readScroll(ref) {
|
|
9321
|
+
assertKind(ref, "snap", "readScroll");
|
|
9322
|
+
return readJsonArtifactFile(ref, "scroll.json");
|
|
9323
|
+
}
|
|
9324
|
+
function readLayers(ref) {
|
|
9325
|
+
assertKind(ref, "snap", "readLayers");
|
|
9326
|
+
return readJsonArtifactFile(ref, "layers.json");
|
|
9327
|
+
}
|
|
9328
|
+
function readStates(ref) {
|
|
9329
|
+
assertKind(ref, "snap", "readStates");
|
|
9330
|
+
return readJsonArtifactFile(ref, "states.json");
|
|
9331
|
+
}
|
|
9332
|
+
function readPixels(ref) {
|
|
9333
|
+
assertKind(ref, "snap", "readPixels");
|
|
9334
|
+
return readJsonArtifactFile(ref, "pixels.json");
|
|
9335
|
+
}
|
|
9336
|
+
function readMedia(ref) {
|
|
9337
|
+
assertKind(ref, "snap", "readMedia");
|
|
9338
|
+
return readJsonArtifactFile(ref, "media.json");
|
|
9339
|
+
}
|
|
9340
|
+
function readRects(ref) {
|
|
9341
|
+
assertKind(ref, "rec", "readRects");
|
|
9342
|
+
return readNdjsonArtifactFile(ref, "rects.jsonl");
|
|
9343
|
+
}
|
|
9344
|
+
function readEvents(ref) {
|
|
9345
|
+
assertKind(ref, "rec", "readEvents");
|
|
9346
|
+
return readNdjsonArtifactFile(ref, "events.jsonl");
|
|
9347
|
+
}
|
|
9348
|
+
function readMarkers(ref) {
|
|
9349
|
+
assertKind(ref, "rec", "readMarkers");
|
|
9350
|
+
return readJsonArtifactFile(ref, "markers.json");
|
|
9351
|
+
}
|
|
9352
|
+
function unstableRegionsFor(ref) {
|
|
9353
|
+
const geometry = readGeometry(ref);
|
|
9354
|
+
return geometry.unstableRegions ?? [];
|
|
9355
|
+
}
|
|
9356
|
+
function rectsOverlap(a, b) {
|
|
9357
|
+
return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
|
|
9358
|
+
}
|
|
9359
|
+
function annotateUnstableFacts(facts, regions) {
|
|
9360
|
+
return facts.map((fact4) => {
|
|
9361
|
+
const caveats = [];
|
|
9362
|
+
for (const region of regions) {
|
|
9363
|
+
const byElement = fact4.elementId !== void 0 && (region.elementIds?.includes(fact4.elementId) ?? false);
|
|
9364
|
+
const byRect = fact4.rect !== void 0 && region.rect !== void 0 && rectsOverlap(fact4.rect, region.rect);
|
|
9365
|
+
if (byElement || byRect) {
|
|
9366
|
+
caveats.push({ regionId: region.id, selector: region.selector, reason: region.reason });
|
|
9367
|
+
}
|
|
9368
|
+
}
|
|
9369
|
+
return { fact: fact4, caveats };
|
|
9370
|
+
});
|
|
9371
|
+
}
|
|
9372
|
+
var fs5, path5, ArtifactResolutionError, SNAP_CONFIG, REC_CONFIG, TRACE_CONFIG, CREATING_COMMAND_BY_FILE;
|
|
9373
|
+
var init_artifact = __esm({
|
|
9374
|
+
"src/output/artifact.ts"() {
|
|
9375
|
+
fs5 = __toESM(require("fs"));
|
|
9376
|
+
path5 = __toESM(require("path"));
|
|
9377
|
+
init_artifacts();
|
|
9378
|
+
init_session_context();
|
|
9379
|
+
ArtifactResolutionError = class extends Error {
|
|
9380
|
+
ref;
|
|
9381
|
+
searched;
|
|
9382
|
+
creatingCommand;
|
|
9383
|
+
constructor(ref, searched, detail, creatingCommand) {
|
|
9384
|
+
const parts = [`could not resolve ref ${JSON.stringify(ref)}: ${detail}`];
|
|
9385
|
+
if (searched.length) parts.push(`looked at: ${searched.join(", ")}`);
|
|
9386
|
+
if (creatingCommand) parts.push(`create it with: ${creatingCommand}`);
|
|
9387
|
+
super(parts.join(" \u2014 "));
|
|
9388
|
+
this.name = "ArtifactResolutionError";
|
|
9389
|
+
this.ref = ref;
|
|
9390
|
+
this.searched = searched;
|
|
9391
|
+
this.creatingCommand = creatingCommand;
|
|
9392
|
+
}
|
|
9393
|
+
};
|
|
9394
|
+
SNAP_CONFIG = {
|
|
9395
|
+
kind: "snap",
|
|
9396
|
+
idSegments: ["measure", "snaps"],
|
|
9397
|
+
creatingCommand: "capture measure snap",
|
|
9398
|
+
label: "snapshot"
|
|
9399
|
+
};
|
|
9400
|
+
REC_CONFIG = {
|
|
9401
|
+
kind: "rec",
|
|
9402
|
+
idSegments: ["motion", "recs"],
|
|
9403
|
+
creatingCommand: "capture motion rec",
|
|
9404
|
+
label: "recording"
|
|
9405
|
+
};
|
|
9406
|
+
TRACE_CONFIG = {
|
|
9407
|
+
kind: "trace",
|
|
9408
|
+
idSegments: ["perf", "traces"],
|
|
9409
|
+
creatingCommand: "capture perf trace",
|
|
9410
|
+
label: "trace"
|
|
9411
|
+
};
|
|
9412
|
+
CREATING_COMMAND_BY_FILE = {
|
|
9413
|
+
"geometry.json": "capture measure snap",
|
|
9414
|
+
"styles.json": "capture measure snap",
|
|
9415
|
+
"hittest.json": "capture measure snap",
|
|
9416
|
+
"text.json": "capture measure snap",
|
|
9417
|
+
"forms.json": "capture measure snap",
|
|
9418
|
+
"animation.json": "capture measure snap",
|
|
9419
|
+
"ax.json": "capture measure snap",
|
|
9420
|
+
"media.json": "capture measure snap",
|
|
9421
|
+
"queries.json": "capture measure snap",
|
|
9422
|
+
"focus.json": "capture measure snap",
|
|
9423
|
+
"scroll.json": "capture measure snap",
|
|
9424
|
+
"layers.json": "capture measure snap",
|
|
9425
|
+
"states.json": "capture measure snap --state <state>",
|
|
9426
|
+
"churn.json": "capture measure snap (written only when the page did not settle)",
|
|
9427
|
+
"pixels.json": "capture measure snap --pixels",
|
|
9428
|
+
"dom.html": "capture measure snap",
|
|
9429
|
+
"screenshot.png": "capture measure snap",
|
|
9430
|
+
"meta.json": "capture measure snap (or capture motion rec for a recording)",
|
|
9431
|
+
"rects.jsonl": "capture motion rec",
|
|
9432
|
+
"events.jsonl": "capture motion rec",
|
|
9433
|
+
"markers.json": "capture motion rec",
|
|
9434
|
+
"trace.json": "capture perf trace"
|
|
9435
|
+
};
|
|
9436
|
+
}
|
|
9437
|
+
});
|
|
9438
|
+
|
|
9098
9439
|
// src/cdp/leaf-grammar.ts
|
|
9099
9440
|
function isParseableUrl(value) {
|
|
9100
9441
|
try {
|
|
@@ -9793,6 +10134,36 @@ function needsEndpointResolution(parsed) {
|
|
|
9793
10134
|
if (parsed.command === "tab" && (parsed.positional[0] === "launch" || parsed.positional[0] === "quit")) return false;
|
|
9794
10135
|
return true;
|
|
9795
10136
|
}
|
|
10137
|
+
function mayReachBrowser(parsed) {
|
|
10138
|
+
const [leaf, sub] = parsed.positional;
|
|
10139
|
+
switch (parsed.command) {
|
|
10140
|
+
case "tab":
|
|
10141
|
+
return leaf !== "list";
|
|
10142
|
+
case "motion":
|
|
10143
|
+
return leaf === "rec";
|
|
10144
|
+
case "perf":
|
|
10145
|
+
return leaf === "trace";
|
|
10146
|
+
case "heap":
|
|
10147
|
+
return leaf === "snapshot";
|
|
10148
|
+
case "measure": {
|
|
10149
|
+
if (leaf === "diff" || leaf === "explain" || leaf === "text" || leaf === "map" && sub === "paint") return false;
|
|
10150
|
+
if (leaf === "check" || leaf === "census" || leaf === "map") return parsed.url !== void 0 || parsed.positional.some(isUrlRef);
|
|
10151
|
+
return true;
|
|
10152
|
+
}
|
|
10153
|
+
default:
|
|
10154
|
+
return true;
|
|
10155
|
+
}
|
|
10156
|
+
}
|
|
10157
|
+
function refuseUnownedEndpoint(parsed, sessionStart) {
|
|
10158
|
+
if (!process.env.CRTR_NODE_ID?.trim() || sessionStart || !mayReachBrowser(parsed)) return;
|
|
10159
|
+
if (parsed.port !== void 0 || parsed.targetSource === "flag" || parsed.targetSource === "session") return;
|
|
10160
|
+
const command = leafCommand(parsed.command, parsed.positional);
|
|
10161
|
+
throw captureError(
|
|
10162
|
+
"precondition",
|
|
10163
|
+
"no_owned_browser",
|
|
10164
|
+
`\`capture ${command}\` was not run: this node (CRTR_NODE_ID) has no browser of its own. With no --port, no --target and no active session holding a browser, capture will not use CDP_PORT, CDP_TARGET or auto-discovery, because the browser it found could belong to another node. Do one of: (1) \`capture session start --url <url>\` to open and hold your own browser tab, or \`capture tab launch\` then \`capture session start --port <port>\`; (2) pass \`--port <port>\` (see \`capture tab list\`); (3) pass \`--target <tabId>\`.`
|
|
10165
|
+
);
|
|
10166
|
+
}
|
|
9796
10167
|
function resolveCliContext(parsed) {
|
|
9797
10168
|
if (!needsEndpointResolution(parsed)) return parsed;
|
|
9798
10169
|
const sessionStart = parsed.command === "session" && parsed.positional[0] === "start";
|
|
@@ -9808,6 +10179,7 @@ function resolveCliContext(parsed) {
|
|
|
9808
10179
|
parsed.portSource = "session";
|
|
9809
10180
|
}
|
|
9810
10181
|
}
|
|
10182
|
+
refuseUnownedEndpoint(parsed, sessionStart);
|
|
9811
10183
|
if (parsed.port === void 0 && process.env.CDP_PORT !== void 0) {
|
|
9812
10184
|
parsed.port = schemaInteger(leafCommand(parsed.command, parsed.positional), process.env.CDP_PORT, "CDP_PORT", 1, 65535);
|
|
9813
10185
|
parsed.portSource = "env";
|
|
@@ -9824,6 +10196,7 @@ var init_args = __esm({
|
|
|
9824
10196
|
"src/cdp/args.ts"() {
|
|
9825
10197
|
init_errors();
|
|
9826
10198
|
init_session_context();
|
|
10199
|
+
init_artifact();
|
|
9827
10200
|
init_leaf_grammar();
|
|
9828
10201
|
VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
9829
10202
|
"--port",
|
|
@@ -9909,7 +10282,7 @@ var init_gate_guard = __esm({
|
|
|
9909
10282
|
|
|
9910
10283
|
// src/session/coordinator.ts
|
|
9911
10284
|
function scopeLifecycleLockPath() {
|
|
9912
|
-
return
|
|
10285
|
+
return path6.join(CAPTURE_ROOT, `.session-lifecycle-${process.env.CRTR_NODE_ID ?? "default"}`);
|
|
9913
10286
|
}
|
|
9914
10287
|
async function withSessionScopeLifecycle(action) {
|
|
9915
10288
|
const lock = await acquirePrivateLock(scopeLifecycleLockPath(), {
|
|
@@ -9923,10 +10296,10 @@ async function withSessionScopeLifecycle(action) {
|
|
|
9923
10296
|
}
|
|
9924
10297
|
}
|
|
9925
10298
|
function statePath(sessionDir2) {
|
|
9926
|
-
return
|
|
10299
|
+
return path6.join(assertUnderCaptureRoot(sessionDir2), ".operations.json");
|
|
9927
10300
|
}
|
|
9928
10301
|
function lockPath(sessionDir2) {
|
|
9929
|
-
return
|
|
10302
|
+
return path6.join(assertUnderCaptureRoot(sessionDir2), ".operations.lock");
|
|
9930
10303
|
}
|
|
9931
10304
|
function isBirth(value) {
|
|
9932
10305
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -9985,7 +10358,7 @@ async function admitSessionOperation(sessionDir2) {
|
|
|
9985
10358
|
};
|
|
9986
10359
|
}
|
|
9987
10360
|
async function beginSessionStop(sessionDir2) {
|
|
9988
|
-
const stopLock = await acquirePrivateLock(
|
|
10361
|
+
const stopLock = await acquirePrivateLock(path6.join(assertUnderCaptureRoot(sessionDir2), ".stop.lock"), {
|
|
9989
10362
|
acquireTimeoutMs: 12e4,
|
|
9990
10363
|
leaseMs: 1e3
|
|
9991
10364
|
});
|
|
@@ -10025,7 +10398,7 @@ async function beginSessionStop(sessionDir2) {
|
|
|
10025
10398
|
};
|
|
10026
10399
|
}
|
|
10027
10400
|
async function withSessionLifecycle(sessionDir2, action, seams2 = {}) {
|
|
10028
|
-
const lock = await acquirePrivateLock(
|
|
10401
|
+
const lock = await acquirePrivateLock(path6.join(assertUnderCaptureRoot(sessionDir2), ".lifecycle.lock"), {
|
|
10029
10402
|
acquireTimeoutMs: seams2.acquireTimeoutMs ?? 3e4,
|
|
10030
10403
|
leaseMs: seams2.leaseMs ?? 3e4,
|
|
10031
10404
|
pidBirthProvider: seams2.pidBirthProvider,
|
|
@@ -10039,11 +10412,11 @@ async function withSessionLifecycle(sessionDir2, action, seams2 = {}) {
|
|
|
10039
10412
|
lock.release();
|
|
10040
10413
|
}
|
|
10041
10414
|
}
|
|
10042
|
-
var crypto3,
|
|
10415
|
+
var crypto3, path6;
|
|
10043
10416
|
var init_coordinator = __esm({
|
|
10044
10417
|
"src/session/coordinator.ts"() {
|
|
10045
10418
|
crypto3 = __toESM(require("node:crypto"));
|
|
10046
|
-
|
|
10419
|
+
path6 = __toESM(require("node:path"));
|
|
10047
10420
|
init_errors();
|
|
10048
10421
|
init_artifacts();
|
|
10049
10422
|
}
|
|
@@ -10571,7 +10944,7 @@ function validateHarAppendBatch(value, source) {
|
|
|
10571
10944
|
return { entries, incompleteLifecycles };
|
|
10572
10945
|
}
|
|
10573
10946
|
function captureVersion() {
|
|
10574
|
-
const version = "1.4.
|
|
10947
|
+
const version = "1.4.23";
|
|
10575
10948
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
10576
10949
|
}
|
|
10577
10950
|
function token() {
|
|
@@ -10589,15 +10962,15 @@ function vStoreHeader(value, source) {
|
|
|
10589
10962
|
return { format: "capture-live-har-ndjson", version: 1, creator: { name: "capture", version } };
|
|
10590
10963
|
}
|
|
10591
10964
|
function harFilePath(id) {
|
|
10592
|
-
if (
|
|
10593
|
-
if (
|
|
10594
|
-
if (id.endsWith(".json") && id.includes(
|
|
10595
|
-
return assertUnderCaptureRoot(
|
|
10965
|
+
if (path7.isAbsolute(id)) return assertUnderCaptureRoot(id);
|
|
10966
|
+
if (path7.basename(id) === id) return assertUnderCaptureRoot(path7.resolve(CAPTURE_ROOT, id));
|
|
10967
|
+
if (id.endsWith(".json") && id.includes(path7.sep)) return assertUnderCaptureRoot(id);
|
|
10968
|
+
return assertUnderCaptureRoot(path7.resolve(id));
|
|
10596
10969
|
}
|
|
10597
10970
|
async function createHarRecording(sessionDir2 = CAPTURE_ROOT) {
|
|
10598
|
-
const sessionAbsolute =
|
|
10971
|
+
const sessionAbsolute = path7.resolve(sessionDir2);
|
|
10599
10972
|
assertUnderCaptureRoot(sessionAbsolute);
|
|
10600
|
-
const harDir =
|
|
10973
|
+
const harDir = path7.join(sessionAbsolute, ".har");
|
|
10601
10974
|
ensurePrivateDir(harDir);
|
|
10602
10975
|
const header = {
|
|
10603
10976
|
format: "capture-live-har-ndjson",
|
|
@@ -10607,7 +10980,7 @@ async function createHarRecording(sessionDir2 = CAPTURE_ROOT) {
|
|
|
10607
10980
|
const headerLine = `${JSON.stringify(header)}
|
|
10608
10981
|
`;
|
|
10609
10982
|
for (; ; ) {
|
|
10610
|
-
const candidate =
|
|
10983
|
+
const candidate = path7.join(harDir, `${token()}.json`);
|
|
10611
10984
|
try {
|
|
10612
10985
|
createPrivateFile(candidate, headerLine);
|
|
10613
10986
|
} catch (error4) {
|
|
@@ -10670,39 +11043,39 @@ async function appendToHarRecording(id, batch) {
|
|
|
10670
11043
|
async function deleteHarRecording(id) {
|
|
10671
11044
|
unlinkPrivateFile(harFilePath(id));
|
|
10672
11045
|
}
|
|
10673
|
-
var import_node_crypto,
|
|
11046
|
+
var import_node_crypto, path7, MAX_BODY_BYTES, HAR_DIR;
|
|
10674
11047
|
var init_har_manager = __esm({
|
|
10675
11048
|
"src/har-manager.ts"() {
|
|
10676
11049
|
import_node_crypto = __toESM(require("node:crypto"));
|
|
10677
|
-
|
|
11050
|
+
path7 = __toESM(require("path"));
|
|
10678
11051
|
init_artifacts();
|
|
10679
11052
|
MAX_BODY_BYTES = 262144;
|
|
10680
|
-
HAR_DIR =
|
|
11053
|
+
HAR_DIR = path7.join(CAPTURE_ROOT, ".har");
|
|
10681
11054
|
}
|
|
10682
11055
|
});
|
|
10683
11056
|
|
|
10684
11057
|
// src/cdp/bridge/spawn.ts
|
|
10685
11058
|
function bridgeSocketPath(sessionDir2) {
|
|
10686
|
-
return
|
|
11059
|
+
return path8.join(sessionDir2, "bridge.sock");
|
|
10687
11060
|
}
|
|
10688
11061
|
function collectorSocketDir() {
|
|
10689
11062
|
const dir = "/tmp/capture-sockets";
|
|
10690
11063
|
try {
|
|
10691
|
-
|
|
11064
|
+
fs6.mkdirSync(dir, { mode: 448 });
|
|
10692
11065
|
} catch (error4) {
|
|
10693
11066
|
if (error4.code !== "EEXIST") throw error4;
|
|
10694
11067
|
}
|
|
10695
|
-
const stat =
|
|
11068
|
+
const stat = fs6.lstatSync(dir);
|
|
10696
11069
|
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error(`collector socket directory is not a real directory: ${dir}`);
|
|
10697
11070
|
if (typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new Error(`collector socket directory is not owned by this user: ${dir}`);
|
|
10698
|
-
|
|
11071
|
+
fs6.chmodSync(dir, 448);
|
|
10699
11072
|
return dir;
|
|
10700
11073
|
}
|
|
10701
11074
|
function shortSocketName(scopeDir) {
|
|
10702
|
-
return crypto5.createHash("sha1").update(
|
|
11075
|
+
return crypto5.createHash("sha1").update(path8.resolve(scopeDir)).digest("hex").slice(0, 16);
|
|
10703
11076
|
}
|
|
10704
11077
|
function collectorHostSocketPath(sessionDir2) {
|
|
10705
|
-
return
|
|
11078
|
+
return path8.join(collectorSocketDir(), `host-${shortSocketName(sessionDir2)}.sock`);
|
|
10706
11079
|
}
|
|
10707
11080
|
async function startBridge(sessionDir2, port, timeoutMs = 5e3) {
|
|
10708
11081
|
const socketPath = bridgeSocketPath(sessionDir2);
|
|
@@ -10718,7 +11091,7 @@ async function startBridge(sessionDir2, port, timeoutMs = 5e3) {
|
|
|
10718
11091
|
throw new Error("Failed to spawn CDP bridge process.");
|
|
10719
11092
|
}
|
|
10720
11093
|
const start2 = Date.now();
|
|
10721
|
-
while (!
|
|
11094
|
+
while (!fs6.existsSync(socketPath)) {
|
|
10722
11095
|
if (Date.now() - start2 > timeoutMs) {
|
|
10723
11096
|
stopBridge(pid, socketPath);
|
|
10724
11097
|
throw new Error(
|
|
@@ -10734,7 +11107,7 @@ async function startCollectorHost(socketPath, port, targetId, sessionDir2, timeo
|
|
|
10734
11107
|
child.unref();
|
|
10735
11108
|
if (!child.pid) throw new Error("Failed to spawn collector host process.");
|
|
10736
11109
|
const started = Date.now();
|
|
10737
|
-
while (!
|
|
11110
|
+
while (!fs6.existsSync(socketPath)) {
|
|
10738
11111
|
if (Date.now() - started > timeoutMs) {
|
|
10739
11112
|
stopBridge(child.pid, socketPath);
|
|
10740
11113
|
throw new Error(`Collector host (pid ${child.pid}) did not come up within ${timeoutMs}ms.`);
|
|
@@ -10752,18 +11125,18 @@ function stopBridge(pid, socketPath) {
|
|
|
10752
11125
|
}
|
|
10753
11126
|
if (socketPath) {
|
|
10754
11127
|
try {
|
|
10755
|
-
if (
|
|
11128
|
+
if (fs6.lstatSync(socketPath).isSocket()) fs6.unlinkSync(socketPath);
|
|
10756
11129
|
} catch {
|
|
10757
11130
|
}
|
|
10758
11131
|
}
|
|
10759
11132
|
}
|
|
10760
|
-
var import_child_process2, crypto5,
|
|
11133
|
+
var import_child_process2, crypto5, fs6, path8;
|
|
10761
11134
|
var init_spawn = __esm({
|
|
10762
11135
|
"src/cdp/bridge/spawn.ts"() {
|
|
10763
11136
|
import_child_process2 = require("child_process");
|
|
10764
11137
|
crypto5 = __toESM(require("crypto"));
|
|
10765
|
-
|
|
10766
|
-
|
|
11138
|
+
fs6 = __toESM(require("fs"));
|
|
11139
|
+
path8 = __toESM(require("path"));
|
|
10767
11140
|
}
|
|
10768
11141
|
});
|
|
10769
11142
|
|
|
@@ -10783,7 +11156,7 @@ function socketDir(create) {
|
|
|
10783
11156
|
}
|
|
10784
11157
|
function socketPathForToken(token2, createDir) {
|
|
10785
11158
|
if (!LOG_TAILER_SOCKET_TOKEN.test(token2)) throw new Error("invalid log tailer socket token");
|
|
10786
|
-
return
|
|
11159
|
+
return path9.join(socketDir(createDir), `${token2}.sock`);
|
|
10787
11160
|
}
|
|
10788
11161
|
function requireRegisteredLogTailer(value) {
|
|
10789
11162
|
const record5 = parseRegisteredLogTailer(value);
|
|
@@ -10804,23 +11177,23 @@ function ownership(record5, provider) {
|
|
|
10804
11177
|
return observed.status === "found" && sameBirth(observed.identity, record5.birth) ? "same" : "gone";
|
|
10805
11178
|
}
|
|
10806
11179
|
function socketIdentity(socketPath) {
|
|
10807
|
-
const stat =
|
|
11180
|
+
const stat = fs7.lstatSync(socketPath, { bigint: true });
|
|
10808
11181
|
if (!stat.isSocket()) throw new Error(`log tailer control path is not a socket: ${socketPath}`);
|
|
10809
11182
|
return { dev: stat.dev.toString(), ino: stat.ino.toString() };
|
|
10810
11183
|
}
|
|
10811
11184
|
function unlinkRecordedSocket(record5) {
|
|
10812
11185
|
let current;
|
|
10813
11186
|
try {
|
|
10814
|
-
current =
|
|
11187
|
+
current = fs7.lstatSync(record5.socketPath, { bigint: true });
|
|
10815
11188
|
} catch (error4) {
|
|
10816
11189
|
if (error4.code === "ENOENT") return;
|
|
10817
11190
|
throw error4;
|
|
10818
11191
|
}
|
|
10819
11192
|
if (!current.isSocket() || current.dev.toString() !== record5.socketDev || current.ino.toString() !== record5.socketIno) return;
|
|
10820
|
-
|
|
11193
|
+
fs7.unlinkSync(record5.socketPath);
|
|
10821
11194
|
}
|
|
10822
11195
|
function closeOwnedFd(fd) {
|
|
10823
|
-
if (fd !== void 0)
|
|
11196
|
+
if (fd !== void 0) fs7.closeSync(fd);
|
|
10824
11197
|
return void 0;
|
|
10825
11198
|
}
|
|
10826
11199
|
function awaitChildExit(child, timeoutMs) {
|
|
@@ -10907,8 +11280,8 @@ function awaitReadiness(child, timeoutMs) {
|
|
|
10907
11280
|
});
|
|
10908
11281
|
}
|
|
10909
11282
|
async function startSessionLogTailer(options) {
|
|
10910
|
-
const logsDir = ensurePrivateDir(
|
|
10911
|
-
const destPath =
|
|
11283
|
+
const logsDir = ensurePrivateDir(path9.join(options.sessionDir, "logs"));
|
|
11284
|
+
const destPath = path9.join(logsDir, `${options.name}.log`);
|
|
10912
11285
|
const socketToken = crypto6.randomBytes(8).toString("hex");
|
|
10913
11286
|
const socketPath = socketPathForToken(socketToken, true);
|
|
10914
11287
|
const nonce = crypto6.randomBytes(24).toString("hex");
|
|
@@ -11133,7 +11506,7 @@ function nonceMatches(expected, candidate) {
|
|
|
11133
11506
|
}
|
|
11134
11507
|
function writeReadiness(message) {
|
|
11135
11508
|
try {
|
|
11136
|
-
|
|
11509
|
+
fs7.writeSync(READINESS_FD, message);
|
|
11137
11510
|
return true;
|
|
11138
11511
|
} catch {
|
|
11139
11512
|
return false;
|
|
@@ -11263,7 +11636,7 @@ async function runLogTailer(argv) {
|
|
|
11263
11636
|
const owned = ownedSocket;
|
|
11264
11637
|
orphanCheck = setInterval(() => {
|
|
11265
11638
|
try {
|
|
11266
|
-
const current =
|
|
11639
|
+
const current = fs7.lstatSync(ownedSocketPath, { bigint: true });
|
|
11267
11640
|
if (current.isSocket() && current.dev.toString() === owned.dev && current.ino.toString() === owned.ino) return;
|
|
11268
11641
|
} catch (error4) {
|
|
11269
11642
|
if (error4.code !== "ENOENT") return;
|
|
@@ -11284,7 +11657,7 @@ async function runLogTailer(argv) {
|
|
|
11284
11657
|
});
|
|
11285
11658
|
reader.on("line", (line4) => {
|
|
11286
11659
|
try {
|
|
11287
|
-
|
|
11660
|
+
fs7.writeSync(1, `${(/* @__PURE__ */ new Date()).toISOString()} ${line4}
|
|
11288
11661
|
`);
|
|
11289
11662
|
} catch {
|
|
11290
11663
|
requestStop?.("fatal");
|
|
@@ -11301,7 +11674,7 @@ async function runLogTailer(argv) {
|
|
|
11301
11674
|
if (!writeReadiness("ready\n")) throw new Error("parent closed the readiness channel");
|
|
11302
11675
|
ready = true;
|
|
11303
11676
|
try {
|
|
11304
|
-
|
|
11677
|
+
fs7.closeSync(READINESS_FD);
|
|
11305
11678
|
} catch {
|
|
11306
11679
|
}
|
|
11307
11680
|
confirmDeadline = setTimeout(() => {
|
|
@@ -11336,7 +11709,7 @@ async function runLogTailer(argv) {
|
|
|
11336
11709
|
if (socketPath && ownedSocket) {
|
|
11337
11710
|
try {
|
|
11338
11711
|
const current = socketIdentity(socketPath);
|
|
11339
|
-
if (current.dev === ownedSocket.dev && current.ino === ownedSocket.ino)
|
|
11712
|
+
if (current.dev === ownedSocket.dev && current.ino === ownedSocket.ino) fs7.unlinkSync(socketPath);
|
|
11340
11713
|
} catch (error4) {
|
|
11341
11714
|
if (error4.code !== "ENOENT") exitCode = 1;
|
|
11342
11715
|
}
|
|
@@ -11344,14 +11717,14 @@ async function runLogTailer(argv) {
|
|
|
11344
11717
|
process.exitCode = exitCode;
|
|
11345
11718
|
}
|
|
11346
11719
|
}
|
|
11347
|
-
var import_node_child_process3, crypto6,
|
|
11720
|
+
var import_node_child_process3, crypto6, fs7, net2, path9, readline, READINESS_FD, WORKER_MS, CONTROL_MESSAGE_LIMIT, WORKER_CHILD_EXIT_TIMEOUT_MS, productionWorld, world;
|
|
11348
11721
|
var init_log_tailer = __esm({
|
|
11349
11722
|
"src/session/log-tailer.ts"() {
|
|
11350
11723
|
import_node_child_process3 = require("node:child_process");
|
|
11351
11724
|
crypto6 = __toESM(require("node:crypto"));
|
|
11352
|
-
|
|
11725
|
+
fs7 = __toESM(require("node:fs"));
|
|
11353
11726
|
net2 = __toESM(require("node:net"));
|
|
11354
|
-
|
|
11727
|
+
path9 = __toESM(require("node:path"));
|
|
11355
11728
|
readline = __toESM(require("node:readline"));
|
|
11356
11729
|
init_errors();
|
|
11357
11730
|
init_artifacts();
|
|
@@ -11373,16 +11746,16 @@ var init_log_tailer = __esm({
|
|
|
11373
11746
|
|
|
11374
11747
|
// src/cdp/host/collectors/heap.ts
|
|
11375
11748
|
function reconstructHeapSnapshot(dir) {
|
|
11376
|
-
const snapshot =
|
|
11377
|
-
if (!
|
|
11378
|
-
const bytes =
|
|
11749
|
+
const snapshot = path10.join(dir, "snapshot.heapsnapshot");
|
|
11750
|
+
if (!fs8.existsSync(snapshot)) return { summary: { bytes: 0 }, files: [] };
|
|
11751
|
+
const bytes = fs8.statSync(snapshot).size;
|
|
11379
11752
|
return { summary: { bytes }, files: [{ name: "snapshot.heapsnapshot", bytes }] };
|
|
11380
11753
|
}
|
|
11381
|
-
var
|
|
11754
|
+
var fs8, path10, HeapCollector;
|
|
11382
11755
|
var init_heap = __esm({
|
|
11383
11756
|
"src/cdp/host/collectors/heap.ts"() {
|
|
11384
|
-
|
|
11385
|
-
|
|
11757
|
+
fs8 = __toESM(require("node:fs"));
|
|
11758
|
+
path10 = __toESM(require("node:path"));
|
|
11386
11759
|
HeapCollector = class {
|
|
11387
11760
|
kind = "heap";
|
|
11388
11761
|
claims = ["heap-snapshot"];
|
|
@@ -11571,10 +11944,10 @@ function matchRule(rules, request) {
|
|
|
11571
11944
|
function headerEntries(headers2) {
|
|
11572
11945
|
return Object.entries(headers2).map(([name, value]) => ({ name, value }));
|
|
11573
11946
|
}
|
|
11574
|
-
var
|
|
11947
|
+
var path11, MAX_BODY_BYTES2, RESOURCE_TYPES, FAIL_REASONS, InterceptCollector;
|
|
11575
11948
|
var init_intercept = __esm({
|
|
11576
11949
|
"src/cdp/host/collectors/intercept.ts"() {
|
|
11577
|
-
|
|
11950
|
+
path11 = __toESM(require("node:path"));
|
|
11578
11951
|
init_artifacts();
|
|
11579
11952
|
MAX_BODY_BYTES2 = 10 * 1024 * 1024;
|
|
11580
11953
|
RESOURCE_TYPES = /* @__PURE__ */ new Set(["Document", "Stylesheet", "Image", "Media", "Font", "Script", "TextTrack", "XHR", "Fetch", "Prefetch", "EventSource", "WebSocket", "Manifest", "SignedExchange", "Ping", "CSPViolationReport", "Preflight", "Other"]);
|
|
@@ -11607,8 +11980,8 @@ var init_intercept = __esm({
|
|
|
11607
11980
|
if (!config3) throw new Error("intercept collector has no rule configuration");
|
|
11608
11981
|
this.context = context;
|
|
11609
11982
|
this.ruleMatches = Array.from({ length: config3.rules.length }, () => 0);
|
|
11610
|
-
writePrivateFile(
|
|
11611
|
-
writePrivateFile(
|
|
11983
|
+
writePrivateFile(path11.join(context.dir, "rules.json"), config3.source);
|
|
11984
|
+
writePrivateFile(path11.join(context.dir, "interceptions.jsonl"), "");
|
|
11612
11985
|
context.client.on("Fetch.requestPaused", this.onPaused);
|
|
11613
11986
|
try {
|
|
11614
11987
|
await context.client.send("Fetch.enable", { patterns: [{ urlPattern: "*", requestStage: "Request" }] });
|
|
@@ -11816,10 +12189,10 @@ async function runBridgeServer(socketPath, port) {
|
|
|
11816
12189
|
installProcessCleanup(cleanup, client);
|
|
11817
12190
|
}
|
|
11818
12191
|
function prepareSocketPath(socketPath) {
|
|
11819
|
-
|
|
12192
|
+
fs9.mkdirSync(path12.dirname(socketPath), { recursive: true });
|
|
11820
12193
|
try {
|
|
11821
|
-
if (!
|
|
11822
|
-
|
|
12194
|
+
if (!fs9.lstatSync(socketPath).isSocket()) throw new Error(`socket path is not a Unix socket: ${socketPath}`);
|
|
12195
|
+
fs9.unlinkSync(socketPath);
|
|
11823
12196
|
} catch (error4) {
|
|
11824
12197
|
if (error4.code !== "ENOENT") throw error4;
|
|
11825
12198
|
}
|
|
@@ -11850,7 +12223,7 @@ function closeNdjsonSocket(server2, socketPath) {
|
|
|
11850
12223
|
} catch {
|
|
11851
12224
|
}
|
|
11852
12225
|
try {
|
|
11853
|
-
if (
|
|
12226
|
+
if (fs9.lstatSync(socketPath).isSocket()) fs9.unlinkSync(socketPath);
|
|
11854
12227
|
} catch {
|
|
11855
12228
|
}
|
|
11856
12229
|
}
|
|
@@ -11873,12 +12246,12 @@ async function resolvePort(port) {
|
|
|
11873
12246
|
const { detectCdpPort: detectCdpPort2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
|
|
11874
12247
|
return detectCdpPort2();
|
|
11875
12248
|
}
|
|
11876
|
-
var
|
|
12249
|
+
var fs9, net3, path12, EventBroker;
|
|
11877
12250
|
var init_server = __esm({
|
|
11878
12251
|
"src/cdp/bridge/server.ts"() {
|
|
11879
|
-
|
|
12252
|
+
fs9 = __toESM(require("fs"));
|
|
11880
12253
|
net3 = __toESM(require("net"));
|
|
11881
|
-
|
|
12254
|
+
path12 = __toESM(require("path"));
|
|
11882
12255
|
init_targets();
|
|
11883
12256
|
EventBroker = class {
|
|
11884
12257
|
constructor(client) {
|
|
@@ -12673,7 +13046,7 @@ function wallTime(value) {
|
|
|
12673
13046
|
}
|
|
12674
13047
|
}
|
|
12675
13048
|
function captureVersion2() {
|
|
12676
|
-
const version = "1.4.
|
|
13049
|
+
const version = "1.4.23";
|
|
12677
13050
|
return typeof version === "string" && version.length > 0 ? version : "development";
|
|
12678
13051
|
}
|
|
12679
13052
|
function deepFreeze(value) {
|
|
@@ -13429,11 +13802,11 @@ function buildTakeRectElementsExpression(nonce, frameIndex) {
|
|
|
13429
13802
|
function buildTakeResizeTargetsExpression(nonce, seq) {
|
|
13430
13803
|
return `(function(){var k='__captureRecorder_'+${JSON.stringify(nonce)};var h=window[k];return h&&h.takeResizeTargets?h.takeResizeTargets(${JSON.stringify(seq)}):[];})()`;
|
|
13431
13804
|
}
|
|
13432
|
-
var crypto7,
|
|
13805
|
+
var crypto7, path13, RECORDER_BINDING_NAME, OBSERVER_INSTALLED_SENTINEL, TRACE_CATEGORIES, SCREENCAST_OPTIONS, MAX_BINDING_PAYLOAD_BYTES, BINDING_RATE_LIMIT_PER_SECOND, MAX_RECT_ELEMENTS, MAX_RECT_GEOMETRY_CANDIDATES, MAX_RECT_TAG_LENGTH, MAX_RECT_STRING_LENGTH, MAX_RECTS_SERIALIZED_BYTES, MAX_RECT_IDENTITY_RESOLUTIONS, MAX_TRACE_EVENTS_PER_BATCH, MAX_TRACE_SERIALIZED_BYTES, BINDING_FIELD_SANITIZERS, RecorderSession, MotionCollector;
|
|
13433
13806
|
var init_motion = __esm({
|
|
13434
13807
|
"src/cdp/host/collectors/motion.ts"() {
|
|
13435
13808
|
crypto7 = __toESM(require("crypto"));
|
|
13436
|
-
|
|
13809
|
+
path13 = __toESM(require("path"));
|
|
13437
13810
|
init_domains();
|
|
13438
13811
|
init_timing();
|
|
13439
13812
|
init_server();
|
|
@@ -13543,9 +13916,9 @@ var init_motion = __esm({
|
|
|
13543
13916
|
constructor(opts) {
|
|
13544
13917
|
this.client = opts.client;
|
|
13545
13918
|
this.recDir = ensurePrivateDir(opts.recDir);
|
|
13546
|
-
this.framesDir = ensurePrivateDir(
|
|
13547
|
-
this.eventsPath =
|
|
13548
|
-
this.rectsPath =
|
|
13919
|
+
this.framesDir = ensurePrivateDir(path13.join(this.recDir, "frames"));
|
|
13920
|
+
this.eventsPath = path13.join(this.recDir, "events.jsonl");
|
|
13921
|
+
this.rectsPath = path13.join(this.recDir, "rects.jsonl");
|
|
13549
13922
|
this.events = new EventBroker(this.client);
|
|
13550
13923
|
}
|
|
13551
13924
|
async start() {
|
|
@@ -14006,7 +14379,7 @@ var init_motion = __esm({
|
|
|
14006
14379
|
this.updateBaselinesPending();
|
|
14007
14380
|
}
|
|
14008
14381
|
const frameName = `frame-${String(frameIndex).padStart(6, "0")}.png`;
|
|
14009
|
-
writeBinaryPrivate(
|
|
14382
|
+
writeBinaryPrivate(path13.join(this.framesDir, frameName), Buffer.from(params.data, "base64"));
|
|
14010
14383
|
try {
|
|
14011
14384
|
const sample = await this.sampleRects(frameIndex);
|
|
14012
14385
|
const elements = this.sanitizeRectSample(sample.facts, sample.backendNodeIds);
|
|
@@ -14406,10 +14779,10 @@ var init_motion = __esm({
|
|
|
14406
14779
|
if (config3.viewport) {
|
|
14407
14780
|
const { width, height } = config3.viewport;
|
|
14408
14781
|
if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) throw new Error("motion viewport must contain positive integer width and height");
|
|
14409
|
-
writeJsonPrivate(
|
|
14782
|
+
writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), { phase: "attempting", targetId: context.targetId, retainOnStop: true });
|
|
14410
14783
|
await context.client.send("Emulation.setDeviceMetricsOverride", { width, height, deviceScaleFactor: 1, mobile: false });
|
|
14411
14784
|
this.viewportApplied = true;
|
|
14412
|
-
writeJsonPrivate(
|
|
14785
|
+
writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), { phase: "applied", targetId: context.targetId, retainOnStop: true });
|
|
14413
14786
|
}
|
|
14414
14787
|
this.har = new HARRecorder(context.client, (batch) => appendToHarRecording(config3.harId, batch));
|
|
14415
14788
|
await this.har.start();
|
|
@@ -14422,9 +14795,9 @@ var init_motion = __esm({
|
|
|
14422
14795
|
try {
|
|
14423
14796
|
await context.client.send("Emulation.clearDeviceMetricsOverride");
|
|
14424
14797
|
this.viewportApplied = false;
|
|
14425
|
-
removeArtifactTree(
|
|
14798
|
+
removeArtifactTree(path13.join(context.dir, "viewport-override.json"));
|
|
14426
14799
|
} catch (cleanupError) {
|
|
14427
|
-
writeJsonPrivate(
|
|
14800
|
+
writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), {
|
|
14428
14801
|
phase: "restore-failed",
|
|
14429
14802
|
targetId: context.targetId,
|
|
14430
14803
|
retainOnStop: true,
|
|
@@ -14468,13 +14841,13 @@ var init_motion = __esm({
|
|
|
14468
14841
|
if (this.viewportApplied && cause.trigger !== "explicit") {
|
|
14469
14842
|
try {
|
|
14470
14843
|
await this.context.client.send("Emulation.clearDeviceMetricsOverride");
|
|
14471
|
-
removeArtifactTree(
|
|
14844
|
+
removeArtifactTree(path13.join(this.context.dir, "viewport-override.json"));
|
|
14472
14845
|
viewportRestored = true;
|
|
14473
14846
|
} catch {
|
|
14474
14847
|
viewportRestored = false;
|
|
14475
14848
|
}
|
|
14476
14849
|
}
|
|
14477
|
-
writeJsonPrivate(
|
|
14850
|
+
writeJsonPrivate(path13.join(this.context.dir, "markers.json"), stopped.markers);
|
|
14478
14851
|
return { summary: { frames: stopped.frameCount, durationMs: stopped.durationMs, eventCount: stopped.eventCount, markers: stopped.markers, state: stopped.frameCount > 0 ? "finalized" : "partial", viewportRestored, viewportRetained: this.viewportApplied && cause.trigger === "explicit" }, files: [] };
|
|
14479
14852
|
}
|
|
14480
14853
|
onDispatch(notice) {
|
|
@@ -14593,26 +14966,26 @@ var init_trace = __esm({
|
|
|
14593
14966
|
|
|
14594
14967
|
// src/cdp/host/kinds.ts
|
|
14595
14968
|
function motionReconstruct(dir) {
|
|
14596
|
-
const framesDir =
|
|
14597
|
-
const frames2 =
|
|
14969
|
+
const framesDir = path14.join(dir, "frames");
|
|
14970
|
+
const frames2 = fs10.existsSync(framesDir) ? fs10.readdirSync(framesDir).filter((name) => name.endsWith(".png")).length : 0;
|
|
14598
14971
|
const bytes = (name) => {
|
|
14599
14972
|
try {
|
|
14600
|
-
return
|
|
14973
|
+
return fs10.statSync(path14.join(dir, name)).size;
|
|
14601
14974
|
} catch {
|
|
14602
14975
|
return 0;
|
|
14603
14976
|
}
|
|
14604
14977
|
};
|
|
14605
|
-
const files = ["events.jsonl", "rects.jsonl", "markers.json"].filter((name) =>
|
|
14978
|
+
const files = ["events.jsonl", "rects.jsonl", "markers.json"].filter((name) => fs10.existsSync(path14.join(dir, name))).map((name) => ({ name, bytes: bytes(name) }));
|
|
14606
14979
|
return { summary: { frames: frames2, state: "orphaned-finalized" }, files };
|
|
14607
14980
|
}
|
|
14608
14981
|
function collectorKind(kind) {
|
|
14609
14982
|
return COLLECTOR_KINDS[kind];
|
|
14610
14983
|
}
|
|
14611
|
-
var
|
|
14984
|
+
var fs10, path14, motion, trace, heap, intercept, COLLECTOR_KINDS;
|
|
14612
14985
|
var init_kinds = __esm({
|
|
14613
14986
|
"src/cdp/host/kinds.ts"() {
|
|
14614
|
-
|
|
14615
|
-
|
|
14987
|
+
fs10 = __toESM(require("node:fs"));
|
|
14988
|
+
path14 = __toESM(require("node:path"));
|
|
14616
14989
|
init_heap();
|
|
14617
14990
|
init_intercept();
|
|
14618
14991
|
init_motion();
|
|
@@ -14646,8 +15019,8 @@ var init_kinds = __esm({
|
|
|
14646
15019
|
return new TraceCollector();
|
|
14647
15020
|
},
|
|
14648
15021
|
reconstruct(dir) {
|
|
14649
|
-
const file =
|
|
14650
|
-
const bytes =
|
|
15022
|
+
const file = path14.join(dir, "trace.json");
|
|
15023
|
+
const bytes = fs10.existsSync(file) ? fs10.statSync(file).size : 0;
|
|
14651
15024
|
return { summary: { events: 0, windowMs: 0, navigations: 0, categories: "devtools-default" }, files: bytes ? [{ name: "trace.json", bytes }] : [] };
|
|
14652
15025
|
}
|
|
14653
15026
|
};
|
|
@@ -14677,12 +15050,12 @@ var init_kinds = __esm({
|
|
|
14677
15050
|
reconstruct(dir) {
|
|
14678
15051
|
const bytes = (name) => {
|
|
14679
15052
|
try {
|
|
14680
|
-
return
|
|
15053
|
+
return fs10.statSync(path14.join(dir, name)).size;
|
|
14681
15054
|
} catch {
|
|
14682
15055
|
return 0;
|
|
14683
15056
|
}
|
|
14684
15057
|
};
|
|
14685
|
-
const files = ["rules.json", "interceptions.jsonl"].filter((name) =>
|
|
15058
|
+
const files = ["rules.json", "interceptions.jsonl"].filter((name) => fs10.existsSync(path14.join(dir, name))).map((name) => ({ name, bytes: bytes(name) }));
|
|
14686
15059
|
return { summary: { state: "orphaned-finalized" }, files };
|
|
14687
15060
|
}
|
|
14688
15061
|
};
|
|
@@ -14697,7 +15070,7 @@ var init_kinds = __esm({
|
|
|
14697
15070
|
|
|
14698
15071
|
// src/cdp/host/handle.ts
|
|
14699
15072
|
function collectorHostPath(sessionDir2) {
|
|
14700
|
-
return
|
|
15073
|
+
return path15.join(assertUnderCaptureRoot(sessionDir2), ".collector-host.json");
|
|
14701
15074
|
}
|
|
14702
15075
|
function parseHandle(raw, sessionDir2) {
|
|
14703
15076
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
@@ -14721,7 +15094,7 @@ function scanCollectorHost(sessionDir2) {
|
|
|
14721
15094
|
if (read.status === "unknown") return { classification: "unknown", handle, path: file };
|
|
14722
15095
|
if (read.status === "found" && sameBirth(read.identity, handle.birth)) {
|
|
14723
15096
|
try {
|
|
14724
|
-
if (!
|
|
15097
|
+
if (!fs11.lstatSync(handle.socketPath).isSocket()) return { classification: "malformed", handle: null, path: file };
|
|
14725
15098
|
} catch {
|
|
14726
15099
|
return { classification: "malformed", handle: null, path: file };
|
|
14727
15100
|
}
|
|
@@ -14729,11 +15102,11 @@ function scanCollectorHost(sessionDir2) {
|
|
|
14729
15102
|
}
|
|
14730
15103
|
return { classification: "dead", handle, path: file };
|
|
14731
15104
|
}
|
|
14732
|
-
var
|
|
15105
|
+
var fs11, path15;
|
|
14733
15106
|
var init_handle = __esm({
|
|
14734
15107
|
"src/cdp/host/handle.ts"() {
|
|
14735
|
-
|
|
14736
|
-
|
|
15108
|
+
fs11 = __toESM(require("node:fs"));
|
|
15109
|
+
path15 = __toESM(require("node:path"));
|
|
14737
15110
|
init_spawn();
|
|
14738
15111
|
init_artifacts();
|
|
14739
15112
|
init_artifacts();
|
|
@@ -14746,7 +15119,7 @@ function isCollectorKind(value) {
|
|
|
14746
15119
|
}
|
|
14747
15120
|
function readCollecting(dir) {
|
|
14748
15121
|
try {
|
|
14749
|
-
const raw = JSON.parse(readPrivateFile(
|
|
15122
|
+
const raw = JSON.parse(readPrivateFile(path16.join(dir, "collecting.json")).toString("utf8"));
|
|
14750
15123
|
if (typeof raw.id !== "string" || !isCollectorKind(raw.kind) || typeof raw.startedAt !== "string") return null;
|
|
14751
15124
|
return { id: raw.id, kind: raw.kind, startedAt: raw.startedAt };
|
|
14752
15125
|
} catch {
|
|
@@ -14758,12 +15131,12 @@ function collectingDirectories(root) {
|
|
|
14758
15131
|
const walk2 = (dir) => {
|
|
14759
15132
|
let entries;
|
|
14760
15133
|
try {
|
|
14761
|
-
entries =
|
|
15134
|
+
entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
14762
15135
|
} catch {
|
|
14763
15136
|
return;
|
|
14764
15137
|
}
|
|
14765
15138
|
if (entries.some((entry) => entry.isFile() && entry.name === "collecting.json")) dirs.push(dir);
|
|
14766
|
-
for (const entry of entries) if (entry.isDirectory()) walk2(
|
|
15139
|
+
for (const entry of entries) if (entry.isDirectory()) walk2(path16.join(dir, entry.name));
|
|
14767
15140
|
};
|
|
14768
15141
|
walk2(root);
|
|
14769
15142
|
return dirs;
|
|
@@ -14776,7 +15149,7 @@ function reapOrphanedCollectors(sessionDir2) {
|
|
|
14776
15149
|
try {
|
|
14777
15150
|
const reconstructed = collectorKind(collecting.kind).reconstruct(dir);
|
|
14778
15151
|
const summary = reconstructed.summary && typeof reconstructed.summary === "object" && !Array.isArray(reconstructed.summary) ? reconstructed.summary : { value: reconstructed.summary };
|
|
14779
|
-
writeJsonPrivate(
|
|
15152
|
+
writeJsonPrivate(path16.join(dir, "meta.json"), {
|
|
14780
15153
|
id: collecting.id,
|
|
14781
15154
|
kind: collecting.kind,
|
|
14782
15155
|
completion: "orphaned",
|
|
@@ -14787,7 +15160,7 @@ function reapOrphanedCollectors(sessionDir2) {
|
|
|
14787
15160
|
summary,
|
|
14788
15161
|
...summary
|
|
14789
15162
|
});
|
|
14790
|
-
unlinkPrivateFile(
|
|
15163
|
+
unlinkPrivateFile(path16.join(dir, "collecting.json"));
|
|
14791
15164
|
outcomes.push({ status: "reaped", id: collecting.id, kind: collecting.kind, dir });
|
|
14792
15165
|
} catch (error4) {
|
|
14793
15166
|
outcomes.push({ status: "terminal", id: collecting.id, kind: collecting.kind, dir, error: error4 instanceof Error ? error4.message : String(error4) });
|
|
@@ -14833,11 +15206,11 @@ async function stopAndReapCollectorHostAtSessionStop(sessionDir2) {
|
|
|
14833
15206
|
return terminal(error4 instanceof Error ? error4.message : String(error4));
|
|
14834
15207
|
}
|
|
14835
15208
|
}
|
|
14836
|
-
var
|
|
15209
|
+
var fs12, path16;
|
|
14837
15210
|
var init_lifecycle = __esm({
|
|
14838
15211
|
"src/cdp/host/lifecycle.ts"() {
|
|
14839
|
-
|
|
14840
|
-
|
|
15212
|
+
fs12 = __toESM(require("node:fs"));
|
|
15213
|
+
path16 = __toESM(require("node:path"));
|
|
14841
15214
|
init_spawn();
|
|
14842
15215
|
init_artifacts();
|
|
14843
15216
|
init_kinds();
|
|
@@ -14992,20 +15365,20 @@ function readBundleManifest(bundlePath) {
|
|
|
14992
15365
|
return parsed;
|
|
14993
15366
|
}
|
|
14994
15367
|
function sessionDir(id, artifactRoot = CAPTURE_ROOT) {
|
|
14995
|
-
return
|
|
15368
|
+
return path17.join(artifactRoot, id);
|
|
14996
15369
|
}
|
|
14997
15370
|
function isSessionStopped(session) {
|
|
14998
|
-
return Boolean(session.stoppedAt) ||
|
|
15371
|
+
return Boolean(session.stoppedAt) || fs13.existsSync(path17.join(session.dir, "bundle.json"));
|
|
14999
15372
|
}
|
|
15000
15373
|
function createOneshotSession(kind, artifactDir) {
|
|
15001
15374
|
const id = `oneshot-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
15002
15375
|
const dir = sessionDir(id, artifactDir === void 0 ? CAPTURE_ROOT : registerArtifactRoot(artifactDir));
|
|
15003
|
-
const artifactsDir = kind === "measure" ?
|
|
15376
|
+
const artifactsDir = kind === "measure" ? path17.join(dir, "measure", "snaps") : kind === "motion" ? path17.join(dir, "motion", "recs") : path17.join(dir, "page");
|
|
15004
15377
|
ensurePrivateDir(artifactsDir);
|
|
15005
15378
|
return { id, dir, kind, artifactsDir };
|
|
15006
15379
|
}
|
|
15007
15380
|
function readSnapMeta(snapDir, fallbackId) {
|
|
15008
|
-
const meta = JSON.parse(
|
|
15381
|
+
const meta = JSON.parse(fs13.readFileSync(path17.join(snapDir, "meta.json"), "utf-8"));
|
|
15009
15382
|
return {
|
|
15010
15383
|
id: meta.id ?? fallbackId,
|
|
15011
15384
|
path: snapDir,
|
|
@@ -15016,7 +15389,7 @@ function readSnapMeta(snapDir, fallbackId) {
|
|
|
15016
15389
|
};
|
|
15017
15390
|
}
|
|
15018
15391
|
function readRecMeta(recDir, fallbackId) {
|
|
15019
|
-
const meta = JSON.parse(
|
|
15392
|
+
const meta = JSON.parse(fs13.readFileSync(path17.join(recDir, "meta.json"), "utf-8"));
|
|
15020
15393
|
return {
|
|
15021
15394
|
id: meta.id ?? fallbackId,
|
|
15022
15395
|
path: recDir,
|
|
@@ -15028,40 +15401,40 @@ function readRecMeta(recDir, fallbackId) {
|
|
|
15028
15401
|
};
|
|
15029
15402
|
}
|
|
15030
15403
|
function collectSnaps(dir) {
|
|
15031
|
-
const snapsRoot =
|
|
15032
|
-
if (!
|
|
15033
|
-
return
|
|
15404
|
+
const snapsRoot = path17.join(dir, "measure", "snaps");
|
|
15405
|
+
if (!fs13.existsSync(snapsRoot)) return [];
|
|
15406
|
+
return fs13.readdirSync(snapsRoot).filter((name) => fs13.existsSync(path17.join(snapsRoot, name, "meta.json"))).map((name) => readSnapMeta(path17.join(snapsRoot, name), name));
|
|
15034
15407
|
}
|
|
15035
15408
|
function collectRecs(dir) {
|
|
15036
|
-
const recsRoot =
|
|
15037
|
-
if (!
|
|
15038
|
-
return
|
|
15409
|
+
const recsRoot = path17.join(dir, "motion", "recs");
|
|
15410
|
+
if (!fs13.existsSync(recsRoot)) return [];
|
|
15411
|
+
return fs13.readdirSync(recsRoot).filter((name) => fs13.existsSync(path17.join(recsRoot, name, "meta.json"))).map((name) => readRecMeta(path17.join(recsRoot, name), name));
|
|
15039
15412
|
}
|
|
15040
15413
|
function collectMocks(dir) {
|
|
15041
|
-
const mocksRoot =
|
|
15042
|
-
if (!
|
|
15043
|
-
return
|
|
15044
|
-
const mockDir =
|
|
15045
|
-
const metaPath =
|
|
15046
|
-
if (!
|
|
15047
|
-
const meta = JSON.parse(
|
|
15414
|
+
const mocksRoot = path17.join(dir, "network", "mocks");
|
|
15415
|
+
if (!fs13.existsSync(mocksRoot)) return [];
|
|
15416
|
+
return fs13.readdirSync(mocksRoot).flatMap((name) => {
|
|
15417
|
+
const mockDir = path17.join(mocksRoot, name);
|
|
15418
|
+
const metaPath = path17.join(mockDir, "meta.json");
|
|
15419
|
+
if (!fs13.existsSync(metaPath)) return [];
|
|
15420
|
+
const meta = JSON.parse(fs13.readFileSync(metaPath, "utf8"));
|
|
15048
15421
|
return [{ id: typeof meta.id === "string" ? meta.id : name, path: mockDir, completion: typeof meta.completion === "string" ? meta.completion : "partial", rules: typeof meta.rules === "number" ? meta.rules : 0, paused: typeof meta.paused === "number" ? meta.paused : 0, matched: typeof meta.matched === "number" ? meta.matched : 0, releasedUnmatched: typeof meta.releasedUnmatched === "number" ? meta.releasedUnmatched : 0 }];
|
|
15049
15422
|
});
|
|
15050
15423
|
}
|
|
15051
15424
|
function readLighthouseReportMeta(reportDir, fallbackId) {
|
|
15052
|
-
const meta = JSON.parse(
|
|
15425
|
+
const meta = JSON.parse(fs13.readFileSync(path17.join(reportDir, "meta.json"), "utf-8"));
|
|
15053
15426
|
if (typeof meta.url !== "string" || typeof meta.finalUrl !== "string" || meta.preset !== "mobile" && meta.preset !== "desktop" || !Array.isArray(meta.categories) || !meta.categories.every((category) => typeof category === "string") || meta.lighthouse !== null && typeof meta.lighthouse !== "string" || meta.axe !== null && typeof meta.axe !== "string") {
|
|
15054
15427
|
throw captureError("artifact", "invalid_lighthouse_report_metadata", `Lighthouse report metadata at ${reportDir} is invalid.`);
|
|
15055
15428
|
}
|
|
15056
15429
|
return { id: typeof meta.id === "string" ? meta.id : fallbackId, path: reportDir, url: meta.url, finalUrl: meta.finalUrl, preset: meta.preset, categories: meta.categories, lighthouse: meta.lighthouse, axe: meta.axe };
|
|
15057
15430
|
}
|
|
15058
15431
|
function collectLighthouseReports(dir) {
|
|
15059
|
-
const reportsRoot =
|
|
15060
|
-
if (!
|
|
15061
|
-
return
|
|
15432
|
+
const reportsRoot = path17.join(dir, "lighthouse", "reports");
|
|
15433
|
+
if (!fs13.existsSync(reportsRoot)) return [];
|
|
15434
|
+
return fs13.readdirSync(reportsRoot).filter((name) => fs13.existsSync(path17.join(reportsRoot, name, "meta.json"))).map((name) => readLighthouseReportMeta(path17.join(reportsRoot, name), name));
|
|
15062
15435
|
}
|
|
15063
15436
|
function sessionMetaPath2(id, artifactRoot = CAPTURE_ROOT) {
|
|
15064
|
-
return
|
|
15437
|
+
return path17.join(sessionDir(id, artifactRoot), ".session.json");
|
|
15065
15438
|
}
|
|
15066
15439
|
function tryReadSession(id, artifactRoot = CAPTURE_ROOT) {
|
|
15067
15440
|
const metaPath = sessionMetaPath2(id, artifactRoot);
|
|
@@ -15183,7 +15556,7 @@ async function start(parsed) {
|
|
|
15183
15556
|
let selectedBrowser = null;
|
|
15184
15557
|
let endpointSelectionReason = null;
|
|
15185
15558
|
try {
|
|
15186
|
-
ensurePrivateDir(
|
|
15559
|
+
ensurePrivateDir(path17.join(dir, "shots"));
|
|
15187
15560
|
acquired.push({ label: "artifact tree", release: () => removeArtifactTree(dir) });
|
|
15188
15561
|
harId = await startWorld.createHar(dir);
|
|
15189
15562
|
const acquiredHarId = harId;
|
|
@@ -15323,8 +15696,8 @@ async function logTail(parsed) {
|
|
|
15323
15696
|
process.exitCode = 1;
|
|
15324
15697
|
return;
|
|
15325
15698
|
}
|
|
15326
|
-
const source =
|
|
15327
|
-
const name = parsed.name ??
|
|
15699
|
+
const source = path17.resolve(sourcePath);
|
|
15700
|
+
const name = parsed.name ?? path17.basename(source, path17.extname(source));
|
|
15328
15701
|
const labelReason = rejectLogLabel(name);
|
|
15329
15702
|
if (labelReason) {
|
|
15330
15703
|
const labelHint = parsed.name === void 0 && labelReason === `exceeds ${MAX_LOG_LABEL_BYTES} bytes` ? " Pass `--name <short>` to choose a shorter destination label." : "";
|
|
@@ -15336,7 +15709,7 @@ async function logTail(parsed) {
|
|
|
15336
15709
|
process.exitCode = 1;
|
|
15337
15710
|
return;
|
|
15338
15711
|
}
|
|
15339
|
-
if (!
|
|
15712
|
+
if (!fs13.existsSync(source)) {
|
|
15340
15713
|
emitResult({
|
|
15341
15714
|
tag: "error",
|
|
15342
15715
|
attrs: { command: "session log", code: "log_file_not_found" },
|
|
@@ -15420,9 +15793,9 @@ async function logTail(parsed) {
|
|
|
15420
15793
|
}
|
|
15421
15794
|
}
|
|
15422
15795
|
async function locateSessionHar(session) {
|
|
15423
|
-
const stopped =
|
|
15796
|
+
const stopped = fs13.existsSync(path17.join(session.dir, "bundle.json"));
|
|
15424
15797
|
if (stopped) {
|
|
15425
|
-
const harPath =
|
|
15798
|
+
const harPath = path17.join(session.dir, "har.json");
|
|
15426
15799
|
let raw;
|
|
15427
15800
|
try {
|
|
15428
15801
|
raw = readPrivateFile(harPath).toString("utf-8");
|
|
@@ -15666,7 +16039,7 @@ function exportHarBody(id, located, parsed) {
|
|
|
15666
16039
|
return true;
|
|
15667
16040
|
}
|
|
15668
16041
|
try {
|
|
15669
|
-
|
|
16042
|
+
fs13.writeFileSync(parsed.out, bytes);
|
|
15670
16043
|
} catch (cause) {
|
|
15671
16044
|
emitResult({ tag: "error", attrs: { command: "session har", code: "body_write_failed" }, summary: fact`The selected response body was not written to ${parsed.out}: ${cause instanceof Error ? cause.message : String(cause)}` }, { json: parsed.json });
|
|
15672
16045
|
process.exitCode = 1;
|
|
@@ -15830,7 +16203,7 @@ function bundleRows(manifest) {
|
|
|
15830
16203
|
function emitStoppedFromManifest(id, sessionDir2, manifest, parsed) {
|
|
15831
16204
|
emitResult({
|
|
15832
16205
|
tag: "session-stopped",
|
|
15833
|
-
attrs: { id, path:
|
|
16206
|
+
attrs: { id, path: path17.join(sessionDir2, "bundle.json") },
|
|
15834
16207
|
summary: fact`Session ${id} stopped after ${manifest.duration}ms; bundle manifest written.`,
|
|
15835
16208
|
sections: [lineList(bundleRows(manifest))],
|
|
15836
16209
|
followUp: fact`capture session view ${id}`
|
|
@@ -15871,7 +16244,7 @@ async function stop(parsed) {
|
|
|
15871
16244
|
let committed = false;
|
|
15872
16245
|
try {
|
|
15873
16246
|
session = readSession(id);
|
|
15874
|
-
const bundlePath =
|
|
16247
|
+
const bundlePath = path17.join(session.dir, "bundle.json");
|
|
15875
16248
|
try {
|
|
15876
16249
|
const manifest2 = readBundleManifest(bundlePath);
|
|
15877
16250
|
if (session.harId) {
|
|
@@ -15900,19 +16273,19 @@ async function stop(parsed) {
|
|
|
15900
16273
|
if (hostTeardown.status === "terminal") {
|
|
15901
16274
|
throw new Error(`collector host teardown failed: ${hostTeardown.error}`);
|
|
15902
16275
|
}
|
|
15903
|
-
const shotsDir =
|
|
15904
|
-
const shots =
|
|
16276
|
+
const shotsDir = path17.join(session.dir, "shots");
|
|
16277
|
+
const shots = fs13.existsSync(shotsDir) ? fs13.readdirSync(shotsDir).filter((name) => name.endsWith(".png") || name.endsWith(".jpg")).map((name) => ({ name, path: path17.join(shotsDir, name) })) : [];
|
|
15905
16278
|
let har2 = null;
|
|
15906
16279
|
if (session.harId) {
|
|
15907
16280
|
const harData = await readHarRecording(session.harId);
|
|
15908
|
-
const harPath =
|
|
16281
|
+
const harPath = path17.join(session.dir, "har.json");
|
|
15909
16282
|
writeJsonPrivate(harPath, harData);
|
|
15910
16283
|
har2 = { id: session.harId, path: harPath, entryCount: harData.log.entries.length };
|
|
15911
16284
|
}
|
|
15912
|
-
const logsDir =
|
|
15913
|
-
const logs =
|
|
15914
|
-
const filePath =
|
|
15915
|
-
const content =
|
|
16285
|
+
const logsDir = path17.join(session.dir, "logs");
|
|
16286
|
+
const logs = fs13.existsSync(logsDir) ? fs13.readdirSync(logsDir).filter((name) => name.endsWith(".log")).map((name) => {
|
|
16287
|
+
const filePath = path17.join(logsDir, name);
|
|
16288
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
15916
16289
|
return { name, path: filePath, lines: content ? content.split("\n").filter(Boolean).length : 0 };
|
|
15917
16290
|
}) : [];
|
|
15918
16291
|
const snaps = collectSnaps(session.dir);
|
|
@@ -15921,7 +16294,7 @@ async function stop(parsed) {
|
|
|
15921
16294
|
const reports = collectLighthouseReports(session.dir);
|
|
15922
16295
|
const knownDirs = /* @__PURE__ */ new Set(["shots", "logs", "lighthouse", "measure", "motion", "network", ".har", ".stop.lock", ".operations.lock"]);
|
|
15923
16296
|
const knownFiles = /* @__PURE__ */ new Set([".session.json", ".operations.json", "har.json", "bundle.json", "bridge.sock"]);
|
|
15924
|
-
const other =
|
|
16297
|
+
const other = fs13.readdirSync(session.dir).filter((name) => !knownDirs.has(name) && !knownFiles.has(name)).map((name) => ({ name, path: path17.join(session.dir, name) }));
|
|
15925
16298
|
const manifest = {
|
|
15926
16299
|
id: session.sessionId,
|
|
15927
16300
|
startedAt: session.startedAt ?? "",
|
|
@@ -15973,7 +16346,7 @@ function list(parsed) {
|
|
|
15973
16346
|
return;
|
|
15974
16347
|
}
|
|
15975
16348
|
if (rejectSurplusPositionals(parsed, "list", 0, "capture session list")) return;
|
|
15976
|
-
const reads = registeredArtifactRoots().flatMap((artifactRoot) =>
|
|
16349
|
+
const reads = registeredArtifactRoots().flatMap((artifactRoot) => fs13.existsSync(artifactRoot) ? fs13.readdirSync(artifactRoot).filter((d) => fs13.existsSync(sessionMetaPath2(d, artifactRoot))).flatMap((d) => {
|
|
15977
16350
|
try {
|
|
15978
16351
|
return [tryReadSession(d, artifactRoot)];
|
|
15979
16352
|
} catch {
|
|
@@ -15983,7 +16356,7 @@ function list(parsed) {
|
|
|
15983
16356
|
const sessions = reads.flatMap((read) => {
|
|
15984
16357
|
if (!read.ok) return [];
|
|
15985
16358
|
const session = read.session;
|
|
15986
|
-
const hasBundled =
|
|
16359
|
+
const hasBundled = fs13.existsSync(path17.join(session.dir, "bundle.json"));
|
|
15987
16360
|
return [{ id: session.sessionId, startedAt: session.startedAt ?? "", url: session.url ?? null, status: hasBundled ? "stopped" : "active" }];
|
|
15988
16361
|
});
|
|
15989
16362
|
const unreadable = reads.flatMap((read) => read.ok ? [] : [read]);
|
|
@@ -16061,7 +16434,7 @@ function view(parsed) {
|
|
|
16061
16434
|
process.exitCode = 1;
|
|
16062
16435
|
return;
|
|
16063
16436
|
}
|
|
16064
|
-
const bundlePath =
|
|
16437
|
+
const bundlePath = path17.join(session.dir, "bundle.json");
|
|
16065
16438
|
let manifest;
|
|
16066
16439
|
try {
|
|
16067
16440
|
manifest = readBundleManifest(bundlePath);
|
|
@@ -16144,11 +16517,11 @@ async function sessionMain(parsed, _args) {
|
|
|
16144
16517
|
return;
|
|
16145
16518
|
}
|
|
16146
16519
|
}
|
|
16147
|
-
var
|
|
16520
|
+
var fs13, path17, VIEW_FILTERS, SECTION_LABELS, SESSION_TAB_ATTACH_TIMEOUT_MS, COMMAND_BLOCK, START_USAGE, STOP_USAGE, LIST_USAGE, VIEW_USAGE, DEFAULT_HAR_ROWS, DEFAULT_FULL_HAR_ROWS, HAR_USAGE, LOG_USAGE, productionStartWorld, startWorld;
|
|
16148
16521
|
var init_commands = __esm({
|
|
16149
16522
|
"src/session/commands.ts"() {
|
|
16150
|
-
|
|
16151
|
-
|
|
16523
|
+
fs13 = __toESM(require("fs"));
|
|
16524
|
+
path17 = __toESM(require("path"));
|
|
16152
16525
|
init_har_manager();
|
|
16153
16526
|
init_session_context();
|
|
16154
16527
|
init_spawn();
|
|
@@ -16507,7 +16880,7 @@ async function startCollectors(parsed, client) {
|
|
|
16507
16880
|
let harRecorder;
|
|
16508
16881
|
if (parsed.har && !routed) {
|
|
16509
16882
|
const harPath = harFilePath(parsed.har);
|
|
16510
|
-
if (!
|
|
16883
|
+
if (!fs14.existsSync(harPath)) {
|
|
16511
16884
|
throw captureError(
|
|
16512
16885
|
"precondition",
|
|
16513
16886
|
"har_missing",
|
|
@@ -16589,10 +16962,10 @@ async function withPageAction(parsed, opts, fn) {
|
|
|
16589
16962
|
client.close();
|
|
16590
16963
|
}
|
|
16591
16964
|
}
|
|
16592
|
-
var
|
|
16965
|
+
var fs14, import_node_perf_hooks2, seams;
|
|
16593
16966
|
var init_connection = __esm({
|
|
16594
16967
|
"src/cdp/connection.ts"() {
|
|
16595
|
-
|
|
16968
|
+
fs14 = __toESM(require("fs"));
|
|
16596
16969
|
import_node_perf_hooks2 = require("node:perf_hooks");
|
|
16597
16970
|
init_client();
|
|
16598
16971
|
init_detect();
|
|
@@ -228542,22 +228915,22 @@ var init_exec_expression = __esm({
|
|
|
228542
228915
|
// src/vault/paths.ts
|
|
228543
228916
|
function vaultLibsDir() {
|
|
228544
228917
|
const candidates = [
|
|
228545
|
-
|
|
228918
|
+
path18.resolve(__dirname, "..", "vault", "libs"),
|
|
228546
228919
|
// built bin: bin/../vault/libs
|
|
228547
|
-
|
|
228920
|
+
path18.resolve(__dirname, "..", "..", "vault", "libs")
|
|
228548
228921
|
// tsx dev: src/vault/../../vault/libs
|
|
228549
228922
|
];
|
|
228550
228923
|
const hit = candidates.find(
|
|
228551
|
-
(p) =>
|
|
228924
|
+
(p) => fs15.existsSync(path18.join(p, "_runtime", "index.ts"))
|
|
228552
228925
|
);
|
|
228553
228926
|
if (!hit) throw new Error(DEV_ONLY_MSG);
|
|
228554
228927
|
return hit;
|
|
228555
228928
|
}
|
|
228556
228929
|
function vaultRepoRoot() {
|
|
228557
|
-
return
|
|
228930
|
+
return path18.dirname(vaultLibsDir());
|
|
228558
228931
|
}
|
|
228559
228932
|
function listLibs() {
|
|
228560
|
-
return
|
|
228933
|
+
return fs15.readdirSync(vaultLibsDir(), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name).sort();
|
|
228561
228934
|
}
|
|
228562
228935
|
function unknownLibMsg(name) {
|
|
228563
228936
|
return `Unknown lib "${name}". Available: ${listLibs().join(", ")}`;
|
|
@@ -228570,11 +228943,11 @@ async function loadEsbuild() {
|
|
|
228570
228943
|
throw new Error(DEV_ONLY_MSG);
|
|
228571
228944
|
});
|
|
228572
228945
|
}
|
|
228573
|
-
var
|
|
228946
|
+
var fs15, path18, DEV_ONLY_MSG, VALID_LIB_NAME;
|
|
228574
228947
|
var init_paths = __esm({
|
|
228575
228948
|
"src/vault/paths.ts"() {
|
|
228576
|
-
|
|
228577
|
-
|
|
228949
|
+
fs15 = __toESM(require("fs"));
|
|
228950
|
+
path18 = __toESM(require("path"));
|
|
228578
228951
|
DEV_ONLY_MSG = "This is a dev-only feature of capture \u2014 run it from a capture checkout\n(vault/ source + `pnpm install`). Not available in the published package.";
|
|
228579
228952
|
VALID_LIB_NAME = /^[A-Za-z0-9_.-]+$/;
|
|
228580
228953
|
}
|
|
@@ -228601,7 +228974,7 @@ function preflightLibs(imports) {
|
|
|
228601
228974
|
let m;
|
|
228602
228975
|
while ((m = LIB_SPECIFIER_RE.exec(imports)) !== null) {
|
|
228603
228976
|
const name = m[2];
|
|
228604
|
-
if (!isValidLibName(name) || !
|
|
228977
|
+
if (!isValidLibName(name) || !fs16.existsSync(path19.join(libs, name, "index.ts"))) {
|
|
228605
228978
|
throw new Error(unknownLibMsg(name));
|
|
228606
228979
|
}
|
|
228607
228980
|
}
|
|
@@ -228617,7 +228990,7 @@ function buildEntry(code) {
|
|
|
228617
228990
|
}
|
|
228618
228991
|
function vaultResolver() {
|
|
228619
228992
|
const libs = vaultLibsDir();
|
|
228620
|
-
const runtime =
|
|
228993
|
+
const runtime = path19.join(libs, "_runtime", "index.ts");
|
|
228621
228994
|
return {
|
|
228622
228995
|
name: "vault",
|
|
228623
228996
|
setup(build) {
|
|
@@ -228629,8 +229002,8 @@ function vaultResolver() {
|
|
|
228629
229002
|
if (!isValidLibName(name)) {
|
|
228630
229003
|
return { errors: [{ text: unknownLibMsg(name) }] };
|
|
228631
229004
|
}
|
|
228632
|
-
const entry =
|
|
228633
|
-
return
|
|
229005
|
+
const entry = path19.join(libs, name, "index.ts");
|
|
229006
|
+
return fs16.existsSync(entry) ? { path: entry } : { errors: [{ text: unknownLibMsg(name) }] };
|
|
228634
229007
|
});
|
|
228635
229008
|
}
|
|
228636
229009
|
};
|
|
@@ -228699,11 +229072,11 @@ async function bundleExec(code) {
|
|
|
228699
229072
|
}
|
|
228700
229073
|
return applyReturnInsert(result.outputFiles[0].text);
|
|
228701
229074
|
}
|
|
228702
|
-
var
|
|
229075
|
+
var fs16, path19, SENTINEL, IMPORT_STMT_RE, LIB_SPECIFIER_RE;
|
|
228703
229076
|
var init_bundle = __esm({
|
|
228704
229077
|
"src/vault/bundle.ts"() {
|
|
228705
|
-
|
|
228706
|
-
|
|
229078
|
+
fs16 = __toESM(require("fs"));
|
|
229079
|
+
path19 = __toESM(require("path"));
|
|
228707
229080
|
init_paths();
|
|
228708
229081
|
init_exec_expression();
|
|
228709
229082
|
SENTINEL = "__CAPTURE_RESULT";
|
|
@@ -228723,7 +229096,7 @@ function emitExecError(parsed, code, summary, followUp) {
|
|
|
228723
229096
|
function spillWholeResult(payload, artifactDir) {
|
|
228724
229097
|
const name = `exec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`;
|
|
228725
229098
|
const session = deps3.getActiveSession();
|
|
228726
|
-
const filePath = session ?
|
|
229099
|
+
const filePath = session ? path20.join(session.dir, "page", name) : path20.join(deps3.createOneshotSession("page", artifactDir).artifactsDir, name);
|
|
228727
229100
|
writePrivateFile(filePath, payload);
|
|
228728
229101
|
return filePath;
|
|
228729
229102
|
}
|
|
@@ -228817,7 +229190,7 @@ async function cmdPageExec(parsed, _args) {
|
|
|
228817
229190
|
);
|
|
228818
229191
|
}
|
|
228819
229192
|
try {
|
|
228820
|
-
code =
|
|
229193
|
+
code = fs17.readFileSync(parsed.file, "utf-8");
|
|
228821
229194
|
} catch (err) {
|
|
228822
229195
|
return emitExecError(
|
|
228823
229196
|
parsed,
|
|
@@ -228915,11 +229288,11 @@ async function cmdPageExec(parsed, _args) {
|
|
|
228915
229288
|
{ json: parsed.json }
|
|
228916
229289
|
);
|
|
228917
229290
|
}
|
|
228918
|
-
var
|
|
229291
|
+
var fs17, path20, GENEROUS_RESULT_CAP, DEFAULT_SETTLE_MS2, DEFAULT_EVALUATION_TIMEOUT_MS, TERMINATE_TIMEOUT_MS, USAGE5, deps3, ExecDeadlineError;
|
|
228919
229292
|
var init_exec = __esm({
|
|
228920
229293
|
"src/cdp/commands/page/exec.ts"() {
|
|
228921
|
-
|
|
228922
|
-
|
|
229294
|
+
fs17 = __toESM(require("fs"));
|
|
229295
|
+
path20 = __toESM(require("path"));
|
|
228923
229296
|
init_connection();
|
|
228924
229297
|
init_errors();
|
|
228925
229298
|
init_exec_expression();
|
|
@@ -229031,7 +229404,7 @@ async function resolveOutPath(parsed) {
|
|
|
229031
229404
|
if (parsed.out) return parsed.out;
|
|
229032
229405
|
const sessionPath = await deps4.nextStepPath("shot", "manual");
|
|
229033
229406
|
if (sessionPath) return sessionPath;
|
|
229034
|
-
return
|
|
229407
|
+
return path21.join(deps4.createOneshotSession("page", parsed.artifactDir).artifactsDir, "shot.png");
|
|
229035
229408
|
}
|
|
229036
229409
|
function round6(value) {
|
|
229037
229410
|
return Math.round(value * 1e6) / 1e6;
|
|
@@ -229269,10 +229642,10 @@ async function cmdPageShot(parsed, _args) {
|
|
|
229269
229642
|
}
|
|
229270
229643
|
emitResult(buildScreenshotResult({ ...result, emulation: parsed.fullPage ? "full-page" : viewport2 ? "viewport" : "none", viewport: viewport2, colorScheme }), { json: parsed.json });
|
|
229271
229644
|
}
|
|
229272
|
-
var
|
|
229645
|
+
var path21, USAGE6, deps4, PNG_SIGNATURE, LEGIBILITY_SCALE_THRESHOLD;
|
|
229273
229646
|
var init_shot = __esm({
|
|
229274
229647
|
"src/cdp/commands/page/shot.ts"() {
|
|
229275
|
-
|
|
229648
|
+
path21 = __toESM(require("path"));
|
|
229276
229649
|
init_connection();
|
|
229277
229650
|
init_screenshot();
|
|
229278
229651
|
init_viewport();
|
|
@@ -230497,7 +230870,7 @@ async function cmdTabNetwork(parsed, _args) {
|
|
|
230497
230870
|
`session ${session.sessionId} has no target to emulate on; open one with \`capture session start --hold --url <url>\` or \`capture tab reset <url>\`.`
|
|
230498
230871
|
);
|
|
230499
230872
|
}
|
|
230500
|
-
if (!session.bridgeSocket || !
|
|
230873
|
+
if (!session.bridgeSocket || !fs18.existsSync(session.bridgeSocket)) {
|
|
230501
230874
|
throw captureError(
|
|
230502
230875
|
"precondition",
|
|
230503
230876
|
"no_held_bridge",
|
|
@@ -230561,10 +230934,10 @@ async function cmdTabNetwork(parsed, _args) {
|
|
|
230561
230934
|
}
|
|
230562
230935
|
emitResult(buildNetworkResult(mode, targetId), { json: parsed.json });
|
|
230563
230936
|
}
|
|
230564
|
-
var
|
|
230937
|
+
var fs18, USAGE15;
|
|
230565
230938
|
var init_network = __esm({
|
|
230566
230939
|
"src/cdp/commands/tab/network.ts"() {
|
|
230567
|
-
|
|
230940
|
+
fs18 = __toESM(require("node:fs"));
|
|
230568
230941
|
init_errors();
|
|
230569
230942
|
init_session_context();
|
|
230570
230943
|
init_coordinator();
|
|
@@ -230675,12 +231048,12 @@ var init_lifecycle2 = __esm({
|
|
|
230675
231048
|
|
|
230676
231049
|
// src/cdp/commands/tab/mock/start.ts
|
|
230677
231050
|
function stagedRulesPath() {
|
|
230678
|
-
return
|
|
231051
|
+
return path22.join(CAPTURE_ROOT, ".mock-inputs", `rules-${crypto8.randomBytes(12).toString("hex")}.json`);
|
|
230679
231052
|
}
|
|
230680
231053
|
function sourceFor(pathname) {
|
|
230681
231054
|
let source;
|
|
230682
231055
|
try {
|
|
230683
|
-
source =
|
|
231056
|
+
source = fs19.readFileSync(pathname);
|
|
230684
231057
|
} catch (error4) {
|
|
230685
231058
|
throw captureError("artifact", "mock_rules_unreadable", `could not read mock rules at ${pathname}: ${error4 instanceof Error ? error4.message : String(error4)}`);
|
|
230686
231059
|
}
|
|
@@ -230724,12 +231097,12 @@ async function cmdTabMockStart(parsed) {
|
|
|
230724
231097
|
}
|
|
230725
231098
|
}
|
|
230726
231099
|
}
|
|
230727
|
-
var crypto8,
|
|
231100
|
+
var crypto8, fs19, path22, HELP;
|
|
230728
231101
|
var init_start = __esm({
|
|
230729
231102
|
"src/cdp/commands/tab/mock/start.ts"() {
|
|
230730
231103
|
crypto8 = __toESM(require("node:crypto"));
|
|
230731
|
-
|
|
230732
|
-
|
|
231104
|
+
fs19 = __toESM(require("node:fs"));
|
|
231105
|
+
path22 = __toESM(require("node:path"));
|
|
230733
231106
|
init_errors();
|
|
230734
231107
|
init_artifacts();
|
|
230735
231108
|
init_render();
|
|
@@ -230890,13 +231263,13 @@ function isAgentVisible(doc) {
|
|
|
230890
231263
|
return AGENT_VISIBLE_VISIBILITIES.has(doc.visibility);
|
|
230891
231264
|
}
|
|
230892
231265
|
function cacheDir() {
|
|
230893
|
-
return
|
|
231266
|
+
return path23.join(vaultRepoRoot(), ".cache", "docs");
|
|
230894
231267
|
}
|
|
230895
231268
|
function schemasPathFor(svc) {
|
|
230896
|
-
return
|
|
231269
|
+
return path23.join(vaultLibsDir(), svc, "schemas.ts");
|
|
230897
231270
|
}
|
|
230898
231271
|
function srcSha(svc) {
|
|
230899
|
-
return (0, import_crypto.createHash)("sha256").update(
|
|
231272
|
+
return (0, import_crypto.createHash)("sha256").update(fs20.readFileSync(schemasPathFor(svc), "utf8")).digest("hex");
|
|
230900
231273
|
}
|
|
230901
231274
|
function toJsonSchema(zodSchema) {
|
|
230902
231275
|
const s = zodSchema;
|
|
@@ -230949,14 +231322,14 @@ async function buildDoc(svc) {
|
|
|
230949
231322
|
return doc;
|
|
230950
231323
|
}
|
|
230951
231324
|
async function getDoc(name) {
|
|
230952
|
-
if (!isValidLibName(name) || !
|
|
231325
|
+
if (!isValidLibName(name) || !fs20.existsSync(schemasPathFor(name))) {
|
|
230953
231326
|
throw new Error(unknownLibMsg(name));
|
|
230954
231327
|
}
|
|
230955
231328
|
const sha = srcSha(name);
|
|
230956
|
-
const cacheFile =
|
|
230957
|
-
if (
|
|
231329
|
+
const cacheFile = path23.join(cacheDir(), `${name}.json`);
|
|
231330
|
+
if (fs20.existsSync(cacheFile)) {
|
|
230958
231331
|
try {
|
|
230959
|
-
const cached = JSON.parse(
|
|
231332
|
+
const cached = JSON.parse(fs20.readFileSync(cacheFile, "utf8"));
|
|
230960
231333
|
if (cached._srcSha === sha) {
|
|
230961
231334
|
delete cached._srcSha;
|
|
230962
231335
|
return cached;
|
|
@@ -230965,8 +231338,8 @@ async function getDoc(name) {
|
|
|
230965
231338
|
}
|
|
230966
231339
|
}
|
|
230967
231340
|
const doc = await buildDoc(name);
|
|
230968
|
-
|
|
230969
|
-
|
|
231341
|
+
fs20.mkdirSync(cacheDir(), { recursive: true });
|
|
231342
|
+
fs20.writeFileSync(
|
|
230970
231343
|
cacheFile,
|
|
230971
231344
|
JSON.stringify({ ...doc, _srcSha: sha }, null, 2)
|
|
230972
231345
|
);
|
|
@@ -230988,11 +231361,11 @@ async function listDocs() {
|
|
|
230988
231361
|
}
|
|
230989
231362
|
return out;
|
|
230990
231363
|
}
|
|
230991
|
-
var
|
|
231364
|
+
var fs20, path23, import_crypto, AGENT_VISIBLE_VISIBILITIES, cjsRequire;
|
|
230992
231365
|
var init_docs = __esm({
|
|
230993
231366
|
"src/vault/docs.ts"() {
|
|
230994
|
-
|
|
230995
|
-
|
|
231367
|
+
fs20 = __toESM(require("fs"));
|
|
231368
|
+
path23 = __toESM(require("path"));
|
|
230996
231369
|
import_crypto = require("crypto");
|
|
230997
231370
|
init_paths();
|
|
230998
231371
|
AGENT_VISIBLE_VISIBILITIES = /* @__PURE__ */ new Set(["public", "chat"]);
|
|
@@ -231300,10 +231673,10 @@ async function cmdLib(parsed, _args) {
|
|
|
231300
231673
|
throw e;
|
|
231301
231674
|
}
|
|
231302
231675
|
}
|
|
231303
|
-
var
|
|
231676
|
+
var path24, COMMAND_BLOCK4, LIB_USAGE, LEAF_USAGE, FUNCTION_NAME_EXACT, FUNCTION_NAME_PREFIX, FUNCTION_NAME_SUBSTRING, SERVICE_NAME_MATCH, FUNCTION_DESC_SUBSTRING, SERVICE_DESC_SUBSTRING, svcName, cap, srcPathFor;
|
|
231304
231677
|
var init_lib = __esm({
|
|
231305
231678
|
"src/cdp/commands/lib.ts"() {
|
|
231306
|
-
|
|
231679
|
+
path24 = __toESM(require("path"));
|
|
231307
231680
|
init_errors();
|
|
231308
231681
|
init_render();
|
|
231309
231682
|
init_paths();
|
|
@@ -231349,7 +231722,7 @@ Effects: local reads only; no CDP connection. Dev checkout required.`
|
|
|
231349
231722
|
SERVICE_DESC_SUBSTRING = 10;
|
|
231350
231723
|
svcName = (doc) => doc.library.replace(/^@vallum\//, "");
|
|
231351
231724
|
cap = (value, maxLength) => value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
|
|
231352
|
-
srcPathFor = (name) =>
|
|
231725
|
+
srcPathFor = (name) => path24.join(vaultLibsDir(), name, "index.ts");
|
|
231353
231726
|
}
|
|
231354
231727
|
});
|
|
231355
231728
|
|
|
@@ -231433,7 +231806,7 @@ async function runBrowserScope(method, params, parsed, timeoutMs, deps23 = DEFAU
|
|
|
231433
231806
|
const flagTarget = parsed.targetSource === "flag" ? parsed.target : void 0;
|
|
231434
231807
|
const session = getActiveSession();
|
|
231435
231808
|
const bridgeSocket = session?.bridgeSocket;
|
|
231436
|
-
const heldBridgeActive = Boolean(bridgeSocket &&
|
|
231809
|
+
const heldBridgeActive = Boolean(bridgeSocket && fs21.existsSync(bridgeSocket));
|
|
231437
231810
|
if (parsed.portSource !== "flag" && bridgeSocket && heldBridgeActive) {
|
|
231438
231811
|
let resp;
|
|
231439
231812
|
try {
|
|
@@ -231561,10 +231934,10 @@ function waitForEventOnce(client, eventName, timeoutMs) {
|
|
|
231561
231934
|
});
|
|
231562
231935
|
});
|
|
231563
231936
|
}
|
|
231564
|
-
var
|
|
231937
|
+
var fs21, DEFAULT_TIMEOUT_MS, GENEROUS_RESULT_CAP2, COMMAND_BLOCK5, HELP3, DEFAULT_BROWSER_DEPS, TAB_SCOPE_RECOVERY;
|
|
231565
231938
|
var init_cdp = __esm({
|
|
231566
231939
|
"src/cdp/commands/cdp.ts"() {
|
|
231567
|
-
|
|
231940
|
+
fs21 = __toESM(require("fs"));
|
|
231568
231941
|
init_errors();
|
|
231569
231942
|
init_session_context();
|
|
231570
231943
|
init_connection();
|
|
@@ -231604,12 +231977,12 @@ Effects:
|
|
|
231604
231977
|
});
|
|
231605
231978
|
|
|
231606
231979
|
// src/cdp/host/core.ts
|
|
231607
|
-
var crypto9,
|
|
231980
|
+
var crypto9, fs22, path25, import_node_perf_hooks3, CollectorHost;
|
|
231608
231981
|
var init_core = __esm({
|
|
231609
231982
|
"src/cdp/host/core.ts"() {
|
|
231610
231983
|
crypto9 = __toESM(require("node:crypto"));
|
|
231611
|
-
|
|
231612
|
-
|
|
231984
|
+
fs22 = __toESM(require("node:fs"));
|
|
231985
|
+
path25 = __toESM(require("node:path"));
|
|
231613
231986
|
import_node_perf_hooks3 = require("node:perf_hooks");
|
|
231614
231987
|
init_artifacts();
|
|
231615
231988
|
init_artifacts();
|
|
@@ -231683,7 +232056,7 @@ var init_core = __esm({
|
|
|
231683
232056
|
this.refuseClaims(collector.claims);
|
|
231684
232057
|
const id = `${entry.idPrefix}-${crypto9.randomBytes(5).toString("hex")}`;
|
|
231685
232058
|
this.starting.set(id, collector.claims);
|
|
231686
|
-
const dir =
|
|
232059
|
+
const dir = path25.join(this.sessionDir, ...entry.idSegments, id);
|
|
231687
232060
|
try {
|
|
231688
232061
|
ensurePrivateDir(dir);
|
|
231689
232062
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -231695,13 +232068,13 @@ var init_core = __esm({
|
|
|
231695
232068
|
id,
|
|
231696
232069
|
targetId: this.identity.targetId,
|
|
231697
232070
|
config: config3,
|
|
231698
|
-
appendRecord: (file, record5) => appendNdjsonPrivate(
|
|
231699
|
-
openChunkFile: (file) => openPrivateChunkWriter(
|
|
232071
|
+
appendRecord: (file, record5) => appendNdjsonPrivate(path25.join(dir, file), record5),
|
|
232072
|
+
openChunkFile: (file) => openPrivateChunkWriter(path25.join(dir, file)),
|
|
231700
232073
|
noteLoss: (reason, detail) => {
|
|
231701
232074
|
live.losses.push({ reason, detail });
|
|
231702
232075
|
}
|
|
231703
232076
|
};
|
|
231704
|
-
writeJsonPrivate(
|
|
232077
|
+
writeJsonPrivate(path25.join(dir, "collecting.json"), { kind, id, startedAt, hostPid: this.identity.pid, hostBirth: this.identity.birth });
|
|
231705
232078
|
await collector.start(context);
|
|
231706
232079
|
this.starting.delete(id);
|
|
231707
232080
|
this.live.set(id, live);
|
|
@@ -231723,10 +232096,10 @@ var init_core = __esm({
|
|
|
231723
232096
|
finalizedFiles(dir) {
|
|
231724
232097
|
const files = [];
|
|
231725
232098
|
const walk2 = (relative2) => {
|
|
231726
|
-
for (const entry of
|
|
231727
|
-
const name =
|
|
232099
|
+
for (const entry of fs22.readdirSync(path25.join(dir, relative2), { withFileTypes: true })) {
|
|
232100
|
+
const name = path25.join(relative2, entry.name);
|
|
231728
232101
|
if (entry.isDirectory()) walk2(name);
|
|
231729
|
-
else if (entry.isFile()) files.push({ name, bytes: readPrivateFile(
|
|
232102
|
+
else if (entry.isFile()) files.push({ name, bytes: readPrivateFile(path25.join(dir, name)).length });
|
|
231730
232103
|
}
|
|
231731
232104
|
};
|
|
231732
232105
|
walk2("");
|
|
@@ -231738,12 +232111,12 @@ var init_core = __esm({
|
|
|
231738
232111
|
}
|
|
231739
232112
|
async drain(live, cause) {
|
|
231740
232113
|
const outcome = await live.collector.drain(cause);
|
|
231741
|
-
unlinkPrivateFile(
|
|
232114
|
+
unlinkPrivateFile(path25.join(live.row.dir, "collecting.json"));
|
|
231742
232115
|
const files = this.finalizedFiles(live.row.dir);
|
|
231743
232116
|
const loss = live.losses[0];
|
|
231744
232117
|
const completion = loss || !cause.clientUsable ? "partial" : "complete";
|
|
231745
232118
|
const summary = outcome.summary && typeof outcome.summary === "object" && !Array.isArray(outcome.summary) ? outcome.summary : { value: outcome.summary };
|
|
231746
|
-
writeJsonPrivate(
|
|
232119
|
+
writeJsonPrivate(path25.join(live.row.dir, "meta.json"), {
|
|
231747
232120
|
id: live.row.id,
|
|
231748
232121
|
kind: live.row.kind,
|
|
231749
232122
|
completion,
|
|
@@ -231844,7 +232217,7 @@ async function runCollectorHost(options) {
|
|
|
231844
232217
|
if (session.targetId !== options.targetId) throw new Error("collector host target does not match session state");
|
|
231845
232218
|
await applyActiveSessionNetworkConditions(client, session, options.targetId);
|
|
231846
232219
|
let server2;
|
|
231847
|
-
const handlePath =
|
|
232220
|
+
const handlePath = path26.join(options.sessionDir, ".collector-host.json");
|
|
231848
232221
|
const publish = (snapshot) => writeJsonPrivate(handlePath, {
|
|
231849
232222
|
pid: process.pid,
|
|
231850
232223
|
birth: birth.identity,
|
|
@@ -231941,11 +232314,11 @@ async function runCollectorHost(options) {
|
|
|
231941
232314
|
cleanup();
|
|
231942
232315
|
}, client);
|
|
231943
232316
|
}
|
|
231944
|
-
var crypto10,
|
|
232317
|
+
var crypto10, path26;
|
|
231945
232318
|
var init_server2 = __esm({
|
|
231946
232319
|
"src/cdp/host/server.ts"() {
|
|
231947
232320
|
crypto10 = __toESM(require("node:crypto"));
|
|
231948
|
-
|
|
232321
|
+
path26 = __toESM(require("node:path"));
|
|
231949
232322
|
init_client();
|
|
231950
232323
|
init_targets();
|
|
231951
232324
|
init_server();
|
|
@@ -238193,8 +238566,8 @@ function round32(value) {
|
|
|
238193
238566
|
return Math.round(value * 1e3) / 1e3;
|
|
238194
238567
|
}
|
|
238195
238568
|
async function captureStateRaster(ctx, client, state, id, borderRect2) {
|
|
238196
|
-
const baselinePath =
|
|
238197
|
-
if (!
|
|
238569
|
+
const baselinePath = path27.join(ctx.dir, "screenshot.png");
|
|
238570
|
+
if (!fs23.existsSync(baselinePath)) return void 0;
|
|
238198
238571
|
const response = await client.send("Page.captureScreenshot", { format: "png" });
|
|
238199
238572
|
const statePng = Buffer.from(response.data, "base64");
|
|
238200
238573
|
const imageName = `states/${sanitizeToken(state) || "state"}-${id}.png`;
|
|
@@ -238208,7 +238581,7 @@ async function captureStateRaster(ctx, client, state, id, borderRect2) {
|
|
|
238208
238581
|
const scaleY = available ? stateImage.height / h : 1;
|
|
238209
238582
|
const paddingCssPx = ctx.stateDiffPaddingCssPx ?? DEFAULT_STATE_DIFF_PADDING_CSS_PX;
|
|
238210
238583
|
const raster = { screenshot: imageName, viewportScale: { available, innerWidth: available ? w : 0, innerHeight: available ? h : 0, scaleX: round32(scaleX), scaleY: round32(scaleY), ...available ? {} : { unavailableReason: "viewport-read-unavailable" } }, diffCrop: { paddingCssPx, borderRect: borderRect2 } };
|
|
238211
|
-
const baseline = import_pngjs3.PNG.sync.read(
|
|
238584
|
+
const baseline = import_pngjs3.PNG.sync.read(fs23.readFileSync(baselinePath));
|
|
238212
238585
|
if (baseline.width !== stateImage.width || baseline.height !== stateImage.height) return { ...raster, diffUnavailableReason: "dimension-mismatch" };
|
|
238213
238586
|
const x0 = Math.max(0, Math.floor((borderRect2.x - paddingCssPx) * scaleX));
|
|
238214
238587
|
const y0 = Math.max(0, Math.floor((borderRect2.y - paddingCssPx) * scaleY));
|
|
@@ -238431,11 +238804,11 @@ async function captureOneElement(ctx, client, selector, index, nodeId, identity3
|
|
|
238431
238804
|
...raster ? { raster } : {}
|
|
238432
238805
|
};
|
|
238433
238806
|
}
|
|
238434
|
-
var
|
|
238807
|
+
var fs23, path27, import_pngjs3, MAX_SELECTOR_MATCHES, MAX_AUTO_ELEMENTS, MAX_STRING_LEN, MAX_AFFECTED_ELEMENTS, DEFAULT_STATE_DIFF_PADDING_CSS_PX, STATE_CAPTURE_SETTLE_FRAMES, CONCRETE_STATES, CONCRETE_STATE_SET, INTERACTIVE_SELECTOR, ELIGIBLE_SELECTORS, PSEUDO_CLASS_MAP, STYLE_PROPS, collectStates;
|
|
238435
238808
|
var init_states = __esm({
|
|
238436
238809
|
"src/cdp/measure/collectors/states.ts"() {
|
|
238437
|
-
|
|
238438
|
-
|
|
238810
|
+
fs23 = __toESM(require("node:fs"));
|
|
238811
|
+
path27 = __toESM(require("node:path"));
|
|
238439
238812
|
import_pngjs3 = __toESM(require_png());
|
|
238440
238813
|
init_pixelmatch();
|
|
238441
238814
|
init_domains();
|
|
@@ -238861,9 +239234,9 @@ document.querySelectorAll('img, video, canvas, svg, iframe').length;`;
|
|
|
238861
239234
|
|
|
238862
239235
|
// src/cdp/measure/snapshot.ts
|
|
238863
239236
|
function resolveScopedArtifactPath(dir, filename) {
|
|
238864
|
-
const target =
|
|
238865
|
-
const rel =
|
|
238866
|
-
const escapesDir = rel === ".." || rel.startsWith(`..${
|
|
239237
|
+
const target = path28.resolve(dir, filename);
|
|
239238
|
+
const rel = path28.relative(dir, target);
|
|
239239
|
+
const escapesDir = rel === ".." || rel.startsWith(`..${path28.sep}`) || path28.isAbsolute(rel);
|
|
238867
239240
|
if (escapesDir) {
|
|
238868
239241
|
throw new Error(`Refusing to write snapshot artifact "${filename}" outside its snap dir ${dir}`);
|
|
238869
239242
|
}
|
|
@@ -238960,7 +239333,7 @@ function boundedClient(client, phase, partialPath, onTimeout, requestTimeoutCapM
|
|
|
238960
239333
|
async function captureSnapshotSubstrate(options) {
|
|
238961
239334
|
const client = options.target.client;
|
|
238962
239335
|
const dir = ensurePrivateDir(options.path);
|
|
238963
|
-
const snapId = options.snapId ??
|
|
239336
|
+
const snapId = options.snapId ?? path28.basename(dir);
|
|
238964
239337
|
const settleTimeoutMs = options.settleTimeout ?? DEFAULT_SETTLE_TIMEOUT_MS;
|
|
238965
239338
|
const quietThresholdMs = options.quietThresholdMs ?? DEFAULT_QUIET_THRESHOLD_MS;
|
|
238966
239339
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
@@ -239022,10 +239395,10 @@ async function captureSnapshotSubstrate(options) {
|
|
|
239022
239395
|
const animationEvidence = await collectAnimationEvidence(phaseClient("settle-evidence"));
|
|
239023
239396
|
const grouped = groupChurnEvidence(churnEvidenceRaw, animationEvidence, settleResult.elapsedMs, settleTimeoutMs);
|
|
239024
239397
|
unstableRegions = grouped.unstableRegions;
|
|
239025
|
-
writeJsonPrivate(
|
|
239398
|
+
writeJsonPrivate(path28.join(dir, "churn.json"), grouped.report);
|
|
239026
239399
|
artifacts.push("churn.json");
|
|
239027
239400
|
if (!captured) {
|
|
239028
|
-
writeJsonPrivate(
|
|
239401
|
+
writeJsonPrivate(path28.join(dir, "animation.json"), animationEvidence);
|
|
239029
239402
|
artifacts.push("animation.json");
|
|
239030
239403
|
}
|
|
239031
239404
|
}
|
|
@@ -239077,7 +239450,7 @@ async function captureSnapshotSubstrate(options) {
|
|
|
239077
239450
|
};
|
|
239078
239451
|
for (const collector of baseline) collectorOutcomes.push(await runCollector(collector));
|
|
239079
239452
|
const screenshotResponse = await phaseClient("baseline-screenshot").send("Page.captureScreenshot", { format: "png" });
|
|
239080
|
-
writeBinaryPrivate(
|
|
239453
|
+
writeBinaryPrivate(path28.join(dir, "screenshot.png"), Buffer.from(screenshotResponse.data, "base64"));
|
|
239081
239454
|
artifacts.push("screenshot.png");
|
|
239082
239455
|
let domHtmlValue;
|
|
239083
239456
|
let domHtmlUnavailableReason;
|
|
@@ -239097,7 +239470,7 @@ async function captureSnapshotSubstrate(options) {
|
|
|
239097
239470
|
if (domHtmlValue === void 0) {
|
|
239098
239471
|
domHtmlFact = { available: false, unavailableReason: domHtmlUnavailableReason };
|
|
239099
239472
|
} else {
|
|
239100
|
-
writePrivateFile(
|
|
239473
|
+
writePrivateFile(path28.join(dir, "dom.html"), domHtmlValue);
|
|
239101
239474
|
artifacts.push("dom.html");
|
|
239102
239475
|
domHtmlFact = { available: true };
|
|
239103
239476
|
}
|
|
@@ -239144,7 +239517,7 @@ async function captureSnapshotSubstrate(options) {
|
|
|
239144
239517
|
...domHtmlFact ? { domHtml: domHtmlFact } : {},
|
|
239145
239518
|
...captured ? { collectors: collectorOutcomes } : {}
|
|
239146
239519
|
};
|
|
239147
|
-
writeJsonPrivate(
|
|
239520
|
+
writeJsonPrivate(path28.join(dir, "meta.json"), meta);
|
|
239148
239521
|
artifacts.push("meta.json");
|
|
239149
239522
|
return {
|
|
239150
239523
|
id: snapId,
|
|
@@ -239163,7 +239536,7 @@ async function captureSnapshotSubstrate(options) {
|
|
|
239163
239536
|
} catch (error4) {
|
|
239164
239537
|
if (error4 instanceof SnapshotCaptureTimeout) {
|
|
239165
239538
|
try {
|
|
239166
|
-
writeJsonPrivate(
|
|
239539
|
+
writeJsonPrivate(path28.join(dir, "capture-timeout.json"), {
|
|
239167
239540
|
schemaVersion: 1,
|
|
239168
239541
|
id: snapId,
|
|
239169
239542
|
status: "timeout",
|
|
@@ -239183,10 +239556,10 @@ async function captureSnapshotSubstrate(options) {
|
|
|
239183
239556
|
await restoreAnimationsOnce();
|
|
239184
239557
|
}
|
|
239185
239558
|
}
|
|
239186
|
-
var
|
|
239559
|
+
var path28, COLLECTORS, COLLECTOR_NAMES, SNAPSHOT_REQUEST_TIMEOUT_MS, SCROLL_COLLECTOR_REQUEST_TIMEOUT_MS, SnapshotCaptureTimeout;
|
|
239187
239560
|
var init_snapshot = __esm({
|
|
239188
239561
|
"src/cdp/measure/snapshot.ts"() {
|
|
239189
|
-
|
|
239562
|
+
path28 = __toESM(require("path"));
|
|
239190
239563
|
init_artifacts();
|
|
239191
239564
|
init_domains();
|
|
239192
239565
|
init_settle();
|
|
@@ -239238,347 +239611,6 @@ var init_snapshot = __esm({
|
|
|
239238
239611
|
}
|
|
239239
239612
|
});
|
|
239240
239613
|
|
|
239241
|
-
// src/output/artifact.ts
|
|
239242
|
-
function isUrlRef(ref) {
|
|
239243
|
-
return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ref);
|
|
239244
|
-
}
|
|
239245
|
-
function stripTrailingSep(p) {
|
|
239246
|
-
return p.length > 1 && (p.endsWith("/") || p.endsWith(path28.sep)) ? p.slice(0, -1) : p;
|
|
239247
|
-
}
|
|
239248
|
-
function resolveRefFromPath(ref, config3) {
|
|
239249
|
-
const resolved = assertUnderCaptureRoot(stripTrailingSep(ref));
|
|
239250
|
-
const metaPath = path28.join(resolved, "meta.json");
|
|
239251
|
-
if (!fs23.existsSync(metaPath)) {
|
|
239252
|
-
throw new ArtifactResolutionError(
|
|
239253
|
-
ref,
|
|
239254
|
-
[resolved],
|
|
239255
|
-
`no ${config3.label} found at this path (missing meta.json)`,
|
|
239256
|
-
config3.creatingCommand
|
|
239257
|
-
);
|
|
239258
|
-
}
|
|
239259
|
-
return { kind: config3.kind, id: path28.basename(resolved), dir: resolved };
|
|
239260
|
-
}
|
|
239261
|
-
function resolveRefFromId(ref, config3) {
|
|
239262
|
-
const active = getActiveSession();
|
|
239263
|
-
if (!active) {
|
|
239264
|
-
throw new ArtifactResolutionError(
|
|
239265
|
-
ref,
|
|
239266
|
-
[],
|
|
239267
|
-
`no active capture session \u2014 a bare id resolves against the active session's ${config3.idSegments.join("/")} directory`,
|
|
239268
|
-
`capture session start (then re-run), or pass the absolute ${config3.label} path printed by ${config3.creatingCommand}`
|
|
239269
|
-
);
|
|
239270
|
-
}
|
|
239271
|
-
const dir = path28.join(active.dir, ...config3.idSegments, ref);
|
|
239272
|
-
const resolved = assertUnderCaptureRoot(dir);
|
|
239273
|
-
const metaPath = path28.join(resolved, "meta.json");
|
|
239274
|
-
if (!fs23.existsSync(metaPath)) {
|
|
239275
|
-
throw new ArtifactResolutionError(
|
|
239276
|
-
ref,
|
|
239277
|
-
[resolved],
|
|
239278
|
-
`no ${config3.label} named ${JSON.stringify(ref)} found in the active session (${active.sessionId})`,
|
|
239279
|
-
config3.creatingCommand
|
|
239280
|
-
);
|
|
239281
|
-
}
|
|
239282
|
-
return { kind: config3.kind, id: ref, dir: resolved };
|
|
239283
|
-
}
|
|
239284
|
-
function rejectRelativePath(ref) {
|
|
239285
|
-
if (!path28.isAbsolute(ref) && (ref.includes("/") || ref.includes(path28.sep))) {
|
|
239286
|
-
throw new ArtifactResolutionError(
|
|
239287
|
-
ref,
|
|
239288
|
-
[],
|
|
239289
|
-
"relative paths are not accepted \u2014 pass an absolute artifact path (as printed by the capturing command) or a bare id"
|
|
239290
|
-
);
|
|
239291
|
-
}
|
|
239292
|
-
}
|
|
239293
|
-
async function resolveSnapRef(ref, options = {}) {
|
|
239294
|
-
if (isUrlRef(ref)) {
|
|
239295
|
-
if (!options.onUrl) {
|
|
239296
|
-
throw new ArtifactResolutionError(ref, [], "this command does not accept a URL target \u2014 pass a snapshot id or path instead");
|
|
239297
|
-
}
|
|
239298
|
-
const result = await options.onUrl(ref);
|
|
239299
|
-
return { kind: "snap", id: result.id, dir: assertUnderCaptureRoot(result.dir) };
|
|
239300
|
-
}
|
|
239301
|
-
if (path28.isAbsolute(ref)) return resolveRefFromPath(ref, SNAP_CONFIG);
|
|
239302
|
-
rejectRelativePath(ref);
|
|
239303
|
-
return resolveRefFromId(ref, SNAP_CONFIG);
|
|
239304
|
-
}
|
|
239305
|
-
function resolveRecRef(ref) {
|
|
239306
|
-
if (isUrlRef(ref)) {
|
|
239307
|
-
throw new ArtifactResolutionError(
|
|
239308
|
-
ref,
|
|
239309
|
-
[],
|
|
239310
|
-
"a recording ref cannot be a URL \u2014 record one first with `capture motion rec <url> --do <action>` or `--start`/`--stop`"
|
|
239311
|
-
);
|
|
239312
|
-
}
|
|
239313
|
-
if (path28.isAbsolute(ref)) return resolveRefFromPath(ref, REC_CONFIG);
|
|
239314
|
-
rejectRelativePath(ref);
|
|
239315
|
-
return resolveRefFromId(ref, REC_CONFIG);
|
|
239316
|
-
}
|
|
239317
|
-
function resolveTraceRef(ref) {
|
|
239318
|
-
if (isUrlRef(ref)) {
|
|
239319
|
-
throw new ArtifactResolutionError(ref, [], "a trace ref cannot be a URL \u2014 record one first with `capture perf trace <url>` or `--start`/`--stop`");
|
|
239320
|
-
}
|
|
239321
|
-
const resolved = path28.isAbsolute(ref) ? resolveRefFromPath(ref, TRACE_CONFIG) : (rejectRelativePath(ref), resolveRefFromId(ref, TRACE_CONFIG));
|
|
239322
|
-
let meta;
|
|
239323
|
-
try {
|
|
239324
|
-
meta = JSON.parse(readPrivateFile(path28.join(resolved.dir, "meta.json")).toString("utf8"));
|
|
239325
|
-
} catch (error4) {
|
|
239326
|
-
throw new ArtifactResolutionError(ref, [path28.join(resolved.dir, "meta.json")], `could not read trace metadata: ${error4 instanceof Error ? error4.message : String(error4)}`, TRACE_CONFIG.creatingCommand);
|
|
239327
|
-
}
|
|
239328
|
-
const completion = meta.completion;
|
|
239329
|
-
if (completion !== "complete" && completion !== "partial" && completion !== "orphaned") {
|
|
239330
|
-
throw new ArtifactResolutionError(ref, [path28.join(resolved.dir, "meta.json")], "trace metadata has no valid completion state", TRACE_CONFIG.creatingCommand);
|
|
239331
|
-
}
|
|
239332
|
-
const inventory = meta.files;
|
|
239333
|
-
const inventoryMalformed = !Array.isArray(inventory);
|
|
239334
|
-
const inventoryMismatch = !inventoryMalformed && inventory.some((entry) => {
|
|
239335
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
|
|
239336
|
-
const item = entry;
|
|
239337
|
-
const name = item.name;
|
|
239338
|
-
const bytes = item.bytes;
|
|
239339
|
-
if (typeof name !== "string" || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0 || name.includes(path28.sep)) return true;
|
|
239340
|
-
try {
|
|
239341
|
-
return fs23.statSync(path28.join(resolved.dir, name)).size !== bytes;
|
|
239342
|
-
} catch {
|
|
239343
|
-
return true;
|
|
239344
|
-
}
|
|
239345
|
-
});
|
|
239346
|
-
const truncatedReason = inventoryMalformed ? "artifact_inventory_missing_or_malformed" : inventoryMismatch ? "artifact_inventory_mismatch" : void 0;
|
|
239347
|
-
return { ...resolved, completion: truncatedReason ? "truncated" : completion, ...truncatedReason ? { reason: truncatedReason } : typeof meta.reason === "string" ? { reason: meta.reason } : {} };
|
|
239348
|
-
}
|
|
239349
|
-
function creatingCommandFor(filename) {
|
|
239350
|
-
return CREATING_COMMAND_BY_FILE[filename];
|
|
239351
|
-
}
|
|
239352
|
-
function artifactPath(ref, filename, opts = {}) {
|
|
239353
|
-
const filePath = assertUnderCaptureRoot(path28.join(ref.dir, filename));
|
|
239354
|
-
const mustExist = opts.mustExist ?? true;
|
|
239355
|
-
if (mustExist && !fs23.existsSync(filePath)) {
|
|
239356
|
-
throw new ArtifactResolutionError(
|
|
239357
|
-
ref.id,
|
|
239358
|
-
[filePath],
|
|
239359
|
-
`${filename} is not present in ${ref.kind === "snap" ? "snapshot" : ref.kind === "rec" ? "recording" : "trace"} ${ref.id}`,
|
|
239360
|
-
creatingCommandFor(filename)
|
|
239361
|
-
);
|
|
239362
|
-
}
|
|
239363
|
-
return filePath;
|
|
239364
|
-
}
|
|
239365
|
-
function artifactExists(ref, filename) {
|
|
239366
|
-
return fs23.existsSync(assertUnderCaptureRoot(path28.join(ref.dir, filename)));
|
|
239367
|
-
}
|
|
239368
|
-
function readJsonArtifactFile(ref, filename) {
|
|
239369
|
-
const filePath = artifactPath(ref, filename);
|
|
239370
|
-
let raw;
|
|
239371
|
-
try {
|
|
239372
|
-
raw = fs23.readFileSync(filePath, "utf-8");
|
|
239373
|
-
} catch (err) {
|
|
239374
|
-
throw new ArtifactResolutionError(
|
|
239375
|
-
ref.id,
|
|
239376
|
-
[filePath],
|
|
239377
|
-
`could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
|
|
239378
|
-
creatingCommandFor(filename)
|
|
239379
|
-
);
|
|
239380
|
-
}
|
|
239381
|
-
try {
|
|
239382
|
-
return JSON.parse(raw);
|
|
239383
|
-
} catch (err) {
|
|
239384
|
-
throw new ArtifactResolutionError(
|
|
239385
|
-
ref.id,
|
|
239386
|
-
[filePath],
|
|
239387
|
-
`${filename} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
239388
|
-
creatingCommandFor(filename)
|
|
239389
|
-
);
|
|
239390
|
-
}
|
|
239391
|
-
}
|
|
239392
|
-
function readNdjsonArtifactFile(ref, filename) {
|
|
239393
|
-
const filePath = artifactPath(ref, filename);
|
|
239394
|
-
let raw;
|
|
239395
|
-
try {
|
|
239396
|
-
raw = fs23.readFileSync(filePath, "utf-8");
|
|
239397
|
-
} catch (err) {
|
|
239398
|
-
throw new ArtifactResolutionError(
|
|
239399
|
-
ref.id,
|
|
239400
|
-
[filePath],
|
|
239401
|
-
`could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
|
|
239402
|
-
creatingCommandFor(filename)
|
|
239403
|
-
);
|
|
239404
|
-
}
|
|
239405
|
-
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
239406
|
-
return lines.map((line4, i) => {
|
|
239407
|
-
try {
|
|
239408
|
-
return JSON.parse(line4);
|
|
239409
|
-
} catch (err) {
|
|
239410
|
-
throw new ArtifactResolutionError(
|
|
239411
|
-
ref.id,
|
|
239412
|
-
[filePath],
|
|
239413
|
-
`${filename} line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
239414
|
-
creatingCommandFor(filename)
|
|
239415
|
-
);
|
|
239416
|
-
}
|
|
239417
|
-
});
|
|
239418
|
-
}
|
|
239419
|
-
function assertKind(ref, kind, fnName) {
|
|
239420
|
-
if (ref.kind !== kind) {
|
|
239421
|
-
throw new TypeError(`${fnName} expects a ${kind} ref, got a ${ref.kind} ref (${ref.id})`);
|
|
239422
|
-
}
|
|
239423
|
-
}
|
|
239424
|
-
function readMeta(ref) {
|
|
239425
|
-
return readJsonArtifactFile(ref, "meta.json");
|
|
239426
|
-
}
|
|
239427
|
-
function readGeometry(ref) {
|
|
239428
|
-
assertKind(ref, "snap", "readGeometry");
|
|
239429
|
-
return readJsonArtifactFile(ref, "geometry.json");
|
|
239430
|
-
}
|
|
239431
|
-
function readStyles(ref) {
|
|
239432
|
-
assertKind(ref, "snap", "readStyles");
|
|
239433
|
-
return readJsonArtifactFile(ref, "styles.json");
|
|
239434
|
-
}
|
|
239435
|
-
function readHittest(ref) {
|
|
239436
|
-
assertKind(ref, "snap", "readHittest");
|
|
239437
|
-
return readJsonArtifactFile(ref, "hittest.json");
|
|
239438
|
-
}
|
|
239439
|
-
function readText(ref) {
|
|
239440
|
-
assertKind(ref, "snap", "readText");
|
|
239441
|
-
return readJsonArtifactFile(ref, "text.json");
|
|
239442
|
-
}
|
|
239443
|
-
function readForms(ref) {
|
|
239444
|
-
assertKind(ref, "snap", "readForms");
|
|
239445
|
-
return readJsonArtifactFile(ref, "forms.json");
|
|
239446
|
-
}
|
|
239447
|
-
function readAnimation(ref) {
|
|
239448
|
-
assertKind(ref, "snap", "readAnimation");
|
|
239449
|
-
return readJsonArtifactFile(ref, "animation.json");
|
|
239450
|
-
}
|
|
239451
|
-
function readAx(ref) {
|
|
239452
|
-
assertKind(ref, "snap", "readAx");
|
|
239453
|
-
return readJsonArtifactFile(ref, "ax.json");
|
|
239454
|
-
}
|
|
239455
|
-
function readQueries(ref) {
|
|
239456
|
-
assertKind(ref, "snap", "readQueries");
|
|
239457
|
-
return readJsonArtifactFile(ref, "queries.json");
|
|
239458
|
-
}
|
|
239459
|
-
function readFocus(ref) {
|
|
239460
|
-
assertKind(ref, "snap", "readFocus");
|
|
239461
|
-
return readJsonArtifactFile(ref, "focus.json");
|
|
239462
|
-
}
|
|
239463
|
-
function readScroll(ref) {
|
|
239464
|
-
assertKind(ref, "snap", "readScroll");
|
|
239465
|
-
return readJsonArtifactFile(ref, "scroll.json");
|
|
239466
|
-
}
|
|
239467
|
-
function readLayers(ref) {
|
|
239468
|
-
assertKind(ref, "snap", "readLayers");
|
|
239469
|
-
return readJsonArtifactFile(ref, "layers.json");
|
|
239470
|
-
}
|
|
239471
|
-
function readStates(ref) {
|
|
239472
|
-
assertKind(ref, "snap", "readStates");
|
|
239473
|
-
return readJsonArtifactFile(ref, "states.json");
|
|
239474
|
-
}
|
|
239475
|
-
function readPixels(ref) {
|
|
239476
|
-
assertKind(ref, "snap", "readPixels");
|
|
239477
|
-
return readJsonArtifactFile(ref, "pixels.json");
|
|
239478
|
-
}
|
|
239479
|
-
function readMedia(ref) {
|
|
239480
|
-
assertKind(ref, "snap", "readMedia");
|
|
239481
|
-
return readJsonArtifactFile(ref, "media.json");
|
|
239482
|
-
}
|
|
239483
|
-
function readRects(ref) {
|
|
239484
|
-
assertKind(ref, "rec", "readRects");
|
|
239485
|
-
return readNdjsonArtifactFile(ref, "rects.jsonl");
|
|
239486
|
-
}
|
|
239487
|
-
function readEvents(ref) {
|
|
239488
|
-
assertKind(ref, "rec", "readEvents");
|
|
239489
|
-
return readNdjsonArtifactFile(ref, "events.jsonl");
|
|
239490
|
-
}
|
|
239491
|
-
function readMarkers(ref) {
|
|
239492
|
-
assertKind(ref, "rec", "readMarkers");
|
|
239493
|
-
return readJsonArtifactFile(ref, "markers.json");
|
|
239494
|
-
}
|
|
239495
|
-
function unstableRegionsFor(ref) {
|
|
239496
|
-
const geometry = readGeometry(ref);
|
|
239497
|
-
return geometry.unstableRegions ?? [];
|
|
239498
|
-
}
|
|
239499
|
-
function rectsOverlap(a, b) {
|
|
239500
|
-
return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
|
|
239501
|
-
}
|
|
239502
|
-
function annotateUnstableFacts(facts, regions) {
|
|
239503
|
-
return facts.map((fact4) => {
|
|
239504
|
-
const caveats = [];
|
|
239505
|
-
for (const region of regions) {
|
|
239506
|
-
const byElement = fact4.elementId !== void 0 && (region.elementIds?.includes(fact4.elementId) ?? false);
|
|
239507
|
-
const byRect = fact4.rect !== void 0 && region.rect !== void 0 && rectsOverlap(fact4.rect, region.rect);
|
|
239508
|
-
if (byElement || byRect) {
|
|
239509
|
-
caveats.push({ regionId: region.id, selector: region.selector, reason: region.reason });
|
|
239510
|
-
}
|
|
239511
|
-
}
|
|
239512
|
-
return { fact: fact4, caveats };
|
|
239513
|
-
});
|
|
239514
|
-
}
|
|
239515
|
-
var fs23, path28, ArtifactResolutionError, SNAP_CONFIG, REC_CONFIG, TRACE_CONFIG, CREATING_COMMAND_BY_FILE;
|
|
239516
|
-
var init_artifact = __esm({
|
|
239517
|
-
"src/output/artifact.ts"() {
|
|
239518
|
-
fs23 = __toESM(require("fs"));
|
|
239519
|
-
path28 = __toESM(require("path"));
|
|
239520
|
-
init_artifacts();
|
|
239521
|
-
init_session_context();
|
|
239522
|
-
ArtifactResolutionError = class extends Error {
|
|
239523
|
-
ref;
|
|
239524
|
-
searched;
|
|
239525
|
-
creatingCommand;
|
|
239526
|
-
constructor(ref, searched, detail, creatingCommand) {
|
|
239527
|
-
const parts = [`could not resolve ref ${JSON.stringify(ref)}: ${detail}`];
|
|
239528
|
-
if (searched.length) parts.push(`looked at: ${searched.join(", ")}`);
|
|
239529
|
-
if (creatingCommand) parts.push(`create it with: ${creatingCommand}`);
|
|
239530
|
-
super(parts.join(" \u2014 "));
|
|
239531
|
-
this.name = "ArtifactResolutionError";
|
|
239532
|
-
this.ref = ref;
|
|
239533
|
-
this.searched = searched;
|
|
239534
|
-
this.creatingCommand = creatingCommand;
|
|
239535
|
-
}
|
|
239536
|
-
};
|
|
239537
|
-
SNAP_CONFIG = {
|
|
239538
|
-
kind: "snap",
|
|
239539
|
-
idSegments: ["measure", "snaps"],
|
|
239540
|
-
creatingCommand: "capture measure snap",
|
|
239541
|
-
label: "snapshot"
|
|
239542
|
-
};
|
|
239543
|
-
REC_CONFIG = {
|
|
239544
|
-
kind: "rec",
|
|
239545
|
-
idSegments: ["motion", "recs"],
|
|
239546
|
-
creatingCommand: "capture motion rec",
|
|
239547
|
-
label: "recording"
|
|
239548
|
-
};
|
|
239549
|
-
TRACE_CONFIG = {
|
|
239550
|
-
kind: "trace",
|
|
239551
|
-
idSegments: ["perf", "traces"],
|
|
239552
|
-
creatingCommand: "capture perf trace",
|
|
239553
|
-
label: "trace"
|
|
239554
|
-
};
|
|
239555
|
-
CREATING_COMMAND_BY_FILE = {
|
|
239556
|
-
"geometry.json": "capture measure snap",
|
|
239557
|
-
"styles.json": "capture measure snap",
|
|
239558
|
-
"hittest.json": "capture measure snap",
|
|
239559
|
-
"text.json": "capture measure snap",
|
|
239560
|
-
"forms.json": "capture measure snap",
|
|
239561
|
-
"animation.json": "capture measure snap",
|
|
239562
|
-
"ax.json": "capture measure snap",
|
|
239563
|
-
"media.json": "capture measure snap",
|
|
239564
|
-
"queries.json": "capture measure snap",
|
|
239565
|
-
"focus.json": "capture measure snap",
|
|
239566
|
-
"scroll.json": "capture measure snap",
|
|
239567
|
-
"layers.json": "capture measure snap",
|
|
239568
|
-
"states.json": "capture measure snap --state <state>",
|
|
239569
|
-
"churn.json": "capture measure snap (written only when the page did not settle)",
|
|
239570
|
-
"pixels.json": "capture measure snap --pixels",
|
|
239571
|
-
"dom.html": "capture measure snap",
|
|
239572
|
-
"screenshot.png": "capture measure snap",
|
|
239573
|
-
"meta.json": "capture measure snap (or capture motion rec for a recording)",
|
|
239574
|
-
"rects.jsonl": "capture motion rec",
|
|
239575
|
-
"events.jsonl": "capture motion rec",
|
|
239576
|
-
"markers.json": "capture motion rec",
|
|
239577
|
-
"trace.json": "capture perf trace"
|
|
239578
|
-
};
|
|
239579
|
-
}
|
|
239580
|
-
});
|
|
239581
|
-
|
|
239582
239614
|
// src/cdp/commands/measure/snap.ts
|
|
239583
239615
|
function generateSnapId() {
|
|
239584
239616
|
return `snap-${Date.now().toString(36)}-${(0, import_crypto2.randomBytes)(4).toString("hex")}`;
|
|
@@ -267481,10 +267513,10 @@ Selecting a noun: session, page, and tab address the live browser; measure, moti
|
|
|
267481
267513
|
|
|
267482
267514
|
I/O contract: flags and positionals on input; one rendered prose block on stdout. --json mirrors the same result as JSON, but the rendered block is the contract. stderr carries in-flight diagnostics only. --artifact-dir <path> selects the root for a session bundle (\`session start\`) or a sessionless artifact-producing leaf's one-shot bundle; leaf help states when it writes one. --out always names one caller-owned file, never a bundle directory.
|
|
267483
267515
|
|
|
267484
|
-
A CDP-enabled browser must be running; \`capture tab list\` is the probe, and \`capture tab launch\` starts one capture owns and reaps when nothing is (never hand-roll a detached browser \u2014 nothing reaps that). Capture never signals a browser it did not start, so attaching to your own with --port is unchanged. CDP_PORT / CDP_TARGET pin the browser + tab for target-selecting commands \u2014 precedence: explicit flag > active session > env. \`session start\` adopts only an explicit --target.`;
|
|
267516
|
+
A CDP-enabled browser must be running; \`capture tab list\` is the probe, and \`capture tab launch\` starts one capture owns and reaps when nothing is (never hand-roll a detached browser \u2014 nothing reaps that). Capture never signals a browser it did not start, so attaching to your own with --port is unchanged. CDP_PORT / CDP_TARGET pin the browser + tab for target-selecting commands \u2014 precedence: explicit flag > active session > env. \`session start\` adopts only an explicit --target. When CRTR_NODE_ID is set (a crtr agent node), a browser command with no --port, no --target and no active session of that node is refused instead of falling back to CDP_PORT, CDP_TARGET or localhost discovery, because the browser found could belong to another node: run \`capture session start --url <url>\`, or pass --port or --target. \`capture tab list\` and commands that only read a recorded artifact are not refused.`;
|
|
267485
267517
|
}
|
|
267486
267518
|
function printVersion() {
|
|
267487
|
-
const declared = "1.4.
|
|
267519
|
+
const declared = "1.4.23";
|
|
267488
267520
|
if (declared) {
|
|
267489
267521
|
console.log(declared);
|
|
267490
267522
|
return;
|