@riddledc/riddle-proof 0.8.35 → 0.8.37

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/dist/cli.cjs CHANGED
@@ -7438,10 +7438,68 @@ function previewDeployResultFromRecord(input) {
7438
7438
  function canRecoverPreviewPublish(error) {
7439
7439
  return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
7440
7440
  }
7441
+ function summarizePreviewDirectory(directory) {
7442
+ const stack = [directory];
7443
+ let fileCount = 0;
7444
+ let totalBytes = 0;
7445
+ while (stack.length) {
7446
+ const current = stack.pop();
7447
+ for (const entry of (0, import_node_fs5.readdirSync)(current, { withFileTypes: true })) {
7448
+ const fullPath = import_node_path5.default.join(current, entry.name);
7449
+ if (entry.isDirectory()) {
7450
+ stack.push(fullPath);
7451
+ continue;
7452
+ }
7453
+ if (!entry.isFile()) continue;
7454
+ const stat = (0, import_node_fs5.statSync)(fullPath);
7455
+ fileCount += 1;
7456
+ totalBytes += stat.size;
7457
+ }
7458
+ }
7459
+ return { file_count: fileCount, total_bytes: totalBytes };
7460
+ }
7461
+ function previewProgressEmitter(config, base) {
7462
+ return async (snapshot) => {
7463
+ if (!config.onPreviewProgress) return;
7464
+ await config.onPreviewProgress({
7465
+ label: base.label,
7466
+ framework: base.framework,
7467
+ directory: base.directory,
7468
+ elapsed_ms: Math.max(0, Date.now() - base.startedAt),
7469
+ warnings: base.warnings?.length ? base.warnings : void 0,
7470
+ ...snapshot
7471
+ });
7472
+ };
7473
+ }
7441
7474
  async function waitForPublishedPreview(config, input) {
7475
+ await input.emitProgress?.({
7476
+ stage: "publish_recovering",
7477
+ id: input.id,
7478
+ file_count: input.localSummary?.file_count,
7479
+ total_bytes: input.localSummary?.total_bytes,
7480
+ publish_error: input.publishError.message,
7481
+ message: `publish returned ${input.publishError.status}; polling preview status`
7482
+ });
7442
7483
  for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
7443
7484
  const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
7485
+ await input.emitProgress?.({
7486
+ stage: "checking_status",
7487
+ id: input.id,
7488
+ attempt,
7489
+ attempts: PREVIEW_PUBLISH_RECOVERY_ATTEMPTS,
7490
+ status: typeof status.status === "string" ? status.status : null,
7491
+ preview_url: typeof status.preview_url === "string" ? status.preview_url : void 0,
7492
+ file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
7493
+ total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
7494
+ });
7444
7495
  if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
7496
+ await input.emitProgress?.({
7497
+ stage: "ready",
7498
+ id: input.id,
7499
+ preview_url: String(status.preview_url),
7500
+ file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
7501
+ total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
7502
+ });
7445
7503
  return previewDeployResultFromRecord({
7446
7504
  record: status,
7447
7505
  id: input.id,
@@ -7463,7 +7521,17 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7463
7521
  if (!directory?.trim()) throw new Error("directory is required");
7464
7522
  if (!label?.trim()) throw new Error("label is required");
7465
7523
  if (framework !== "spa" && framework !== "static") throw new Error("framework must be spa or static");
7524
+ const startedAt = Date.now();
7466
7525
  const warnings = collectRiddlePreviewDeployWarnings(directory, framework);
7526
+ const emitProgress = previewProgressEmitter(config, { label, framework, directory, startedAt, warnings });
7527
+ await emitProgress({ stage: "validating", message: "checking preview input directory" });
7528
+ const localSummary = summarizePreviewDirectory(directory);
7529
+ await emitProgress({
7530
+ stage: "creating",
7531
+ file_count: localSummary.file_count,
7532
+ total_bytes: localSummary.total_bytes,
7533
+ message: "creating preview upload target"
7534
+ });
7467
7535
  const created = await riddleRequestJson(config, "/v1/preview", {
7468
7536
  method: "POST",
7469
7537
  body: JSON.stringify({ framework, label })
@@ -7471,10 +7539,42 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7471
7539
  const id = String(created.id || "");
7472
7540
  const uploadUrl = String(created.upload_url || "");
7473
7541
  if (!id || !uploadUrl) throw new Error("Riddle preview create response was missing id or upload_url.");
7542
+ await emitProgress({
7543
+ stage: "created",
7544
+ id,
7545
+ file_count: localSummary.file_count,
7546
+ total_bytes: localSummary.total_bytes,
7547
+ message: "preview upload target created"
7548
+ });
7474
7549
  const scratch = (0, import_node_fs5.mkdtempSync)(import_node_path5.default.join((0, import_node_os2.tmpdir)(), "riddle-preview-upload-"));
7475
7550
  const tarball = import_node_path5.default.join(scratch, `${id}.tar.gz`);
7551
+ let tarballBytes = 0;
7476
7552
  try {
7553
+ await emitProgress({
7554
+ stage: "archiving",
7555
+ id,
7556
+ file_count: localSummary.file_count,
7557
+ total_bytes: localSummary.total_bytes,
7558
+ message: "creating preview archive"
7559
+ });
7477
7560
  (0, import_node_child_process4.execFileSync)("tar", ["czf", tarball, "-C", directory, "."], { stdio: "pipe" });
7561
+ tarballBytes = (0, import_node_fs5.statSync)(tarball).size;
7562
+ await emitProgress({
7563
+ stage: "archived",
7564
+ id,
7565
+ file_count: localSummary.file_count,
7566
+ total_bytes: localSummary.total_bytes,
7567
+ tarball_bytes: tarballBytes,
7568
+ message: "preview archive created"
7569
+ });
7570
+ await emitProgress({
7571
+ stage: "uploading",
7572
+ id,
7573
+ file_count: localSummary.file_count,
7574
+ total_bytes: localSummary.total_bytes,
7575
+ tarball_bytes: tarballBytes,
7576
+ message: "uploading preview archive"
7577
+ });
7478
7578
  const upload = await fetchFor(config)(uploadUrl, {
7479
7579
  method: "PUT",
7480
7580
  headers: { "Content-Type": "application/gzip" },
@@ -7483,14 +7583,37 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7483
7583
  if (!upload.ok) {
7484
7584
  throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
7485
7585
  }
7586
+ await emitProgress({
7587
+ stage: "uploaded",
7588
+ id,
7589
+ file_count: localSummary.file_count,
7590
+ total_bytes: localSummary.total_bytes,
7591
+ tarball_bytes: tarballBytes,
7592
+ message: "preview archive uploaded"
7593
+ });
7486
7594
  } finally {
7487
7595
  (0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
7488
7596
  }
7489
7597
  const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
7490
7598
  try {
7599
+ await emitProgress({
7600
+ stage: "publishing",
7601
+ id,
7602
+ file_count: localSummary.file_count,
7603
+ total_bytes: localSummary.total_bytes,
7604
+ tarball_bytes: tarballBytes || void 0,
7605
+ message: "publishing preview"
7606
+ });
7491
7607
  const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
7492
7608
  method: "POST"
7493
7609
  });
7610
+ await emitProgress({
7611
+ stage: "ready",
7612
+ id,
7613
+ preview_url: String(published.preview_url || ""),
7614
+ file_count: typeof published.file_count === "number" ? published.file_count : localSummary.file_count,
7615
+ total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : localSummary.total_bytes
7616
+ });
7494
7617
  return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt, warnings });
7495
7618
  } catch (error) {
7496
7619
  if (!canRecoverPreviewPublish(error)) throw error;
@@ -7500,7 +7623,9 @@ async function deployRiddlePreview(config, directory, label, framework = "static
7500
7623
  framework,
7501
7624
  expiresAt,
7502
7625
  publishError: error,
7503
- warnings
7626
+ warnings,
7627
+ localSummary,
7628
+ emitProgress
7504
7629
  });
7505
7630
  }
7506
7631
  }
@@ -9147,6 +9272,8 @@ function normalizeSetupAction(input, index) {
9147
9272
  expect_changed: booleanValue(valueFromOwn(input, "expect_changed", "expectChanged", "should_change", "shouldChange", "changed")),
9148
9273
  until_path: untilPath,
9149
9274
  until_expected_value: hasUntilExpectedValue ? toJsonValue(valueFromOwn(input, "until_expected_value", "untilExpectedValue", "until_expected", "untilExpected", "until_value", "untilValue", "expected_value", "expectedValue", "expected")) : void 0,
9275
+ expected_path: stringFromOwn(input, "expected_path", "expectedPath", "expected_terminal_path", "expectedTerminalPath"),
9276
+ expected_url: stringFromOwn(input, "expected_url", "expectedUrl", "expected_terminal_url", "expectedTerminalUrl"),
9150
9277
  max_calls: maxCalls,
9151
9278
  tap_burst_size: tapBurstSize,
9152
9279
  settle_ms: settleMs,
@@ -11488,6 +11615,80 @@ function routePathMatches(observed, expected, targetUrl) {
11488
11615
  if (normalizedObserved === normalizedExpected) return true;
11489
11616
  return normalizedObserved === normalizeRoutePath(mountedExpectedRoutePath(targetUrl, expected));
11490
11617
  }
11618
+ function setupActionExpectedRoute(action) {
11619
+ const expectedUrl = typeof action.expected_url === "string" && action.expected_url.trim()
11620
+ ? action.expected_url.trim()
11621
+ : typeof action.expectedUrl === "string" && action.expectedUrl.trim()
11622
+ ? action.expectedUrl.trim()
11623
+ : "";
11624
+ const expectedPath = typeof action.expected_path === "string" && action.expected_path.trim()
11625
+ ? action.expected_path.trim()
11626
+ : typeof action.expectedPath === "string" && action.expectedPath.trim()
11627
+ ? action.expectedPath.trim()
11628
+ : "";
11629
+ if (!expectedUrl && !expectedPath) return null;
11630
+ return { expected_url: expectedUrl || undefined, expected_path: expectedPath || undefined };
11631
+ }
11632
+ function setupUrlMatchesExpectedRoute(href, expected) {
11633
+ if (!expected) return true;
11634
+ let observedUrl;
11635
+ try {
11636
+ observedUrl = new URL(href, targetUrl);
11637
+ } catch {
11638
+ return false;
11639
+ }
11640
+ if (expected.expected_url) {
11641
+ let expectedUrl;
11642
+ try {
11643
+ expectedUrl = new URL(expected.expected_url, targetUrl);
11644
+ } catch {
11645
+ return false;
11646
+ }
11647
+ return observedUrl.href === expectedUrl.href;
11648
+ }
11649
+ const expectedPath = expected.expected_path || "/";
11650
+ if (/[?#]/.test(expectedPath)) {
11651
+ const observedRoute = observedUrl.pathname + observedUrl.search + observedUrl.hash;
11652
+ const normalizedObservedRoute = observedRoute === "/" ? "/" : observedRoute.replace(/\/+(?=[?#]|$)/, "");
11653
+ const normalizedExpectedRoute = expectedPath === "/" ? "/" : expectedPath.replace(/\/+(?=[?#]|$)/, "");
11654
+ return normalizedObservedRoute === normalizedExpectedRoute;
11655
+ }
11656
+ return routePathMatches(observedUrl.pathname, expectedPath, targetUrl);
11657
+ }
11658
+ function setupObservedRouteEvidence(expected, waitError) {
11659
+ let observedUrl = page.url();
11660
+ let observedPath = "";
11661
+ let observedRoute = "";
11662
+ try {
11663
+ const url = new URL(observedUrl, targetUrl);
11664
+ observedUrl = url.href;
11665
+ observedPath = url.pathname;
11666
+ observedRoute = url.pathname + url.search + url.hash;
11667
+ } catch {
11668
+ observedPath = "";
11669
+ observedRoute = "";
11670
+ }
11671
+ return {
11672
+ expected_url: expected && expected.expected_url || undefined,
11673
+ expected_path: expected && expected.expected_path || undefined,
11674
+ observed_url: observedUrl,
11675
+ observed_path: observedPath,
11676
+ observed_route: observedRoute,
11677
+ route_matched: setupUrlMatchesExpectedRoute(observedUrl, expected),
11678
+ route_wait_error: waitError ? String(waitError && waitError.message ? waitError.message : waitError).slice(0, 1000) : undefined,
11679
+ };
11680
+ }
11681
+ async function waitForSetupActionRoute(action, timeout) {
11682
+ const expected = setupActionExpectedRoute(action);
11683
+ if (!expected) return null;
11684
+ let waitError;
11685
+ try {
11686
+ await page.waitForURL((url) => setupUrlMatchesExpectedRoute(url.href, expected), { timeout: Math.min(timeout, 20000) });
11687
+ } catch (error) {
11688
+ waitError = error;
11689
+ }
11690
+ return setupObservedRouteEvidence(expected, waitError);
11691
+ }
11491
11692
  function routeOk(route, targetUrl) {
11492
11693
  return Boolean(route && (route.matched || routePathMatches(route.observed, route.expected_path, targetUrl)) && !route.error && (route.http_status == null || route.http_status < 400));
11493
11694
  }
@@ -14381,11 +14582,22 @@ async function executeSetupAction(action, ordinal, viewport) {
14381
14582
  const prepared = await resolveSetupTapTarget(action, base, scope, timeout);
14382
14583
  if (prepared.result) return prepared.result;
14383
14584
  await dispatchSetupTapPoint(prepared.target.point, prepared.target.pointerType, prepared.target.durationMs);
14585
+ const routeEvidence = await waitForSetupActionRoute(action, timeout);
14586
+ if (routeEvidence && !routeEvidence.route_matched) {
14587
+ return {
14588
+ ...base,
14589
+ ...setupScopeEvidence(scope),
14590
+ ...setupTapTargetEvidence(prepared.target),
14591
+ ...routeEvidence,
14592
+ reason: "expected_route_not_reached",
14593
+ };
14594
+ }
14384
14595
  return {
14385
14596
  ...base,
14386
14597
  ...setupScopeEvidence(scope),
14387
14598
  ok: true,
14388
14599
  ...setupTapTargetEvidence(prepared.target),
14600
+ ...routeEvidence,
14389
14601
  };
14390
14602
  }
14391
14603
  if (type === "tap_until") {
@@ -15246,6 +15458,26 @@ async function executeSetupAction(action, ordinal, viewport) {
15246
15458
  : { x: box.x + box.width / 2, y: box.y + box.height / 2 };
15247
15459
  if (clickCount > 1) await page.mouse.click(fallbackPoint.x, fallbackPoint.y, { clickCount });
15248
15460
  else await page.mouse.click(fallbackPoint.x, fallbackPoint.y);
15461
+ const routeEvidence = await waitForSetupActionRoute(action, timeout);
15462
+ if (routeEvidence && !routeEvidence.route_matched) {
15463
+ return {
15464
+ ...base,
15465
+ ...setupScopeEvidence(scope),
15466
+ count,
15467
+ target_index: targetIndex,
15468
+ text: matchedText,
15469
+ force: action.force === true || undefined,
15470
+ fallback_to_tap: true,
15471
+ input_dispatch: "playwright_mouse",
15472
+ click_error: String(error && error.message ? error.message : error).slice(0, 1000),
15473
+ click_count: clickCount > 1 ? clickCount : undefined,
15474
+ coordinate_mode: mode,
15475
+ x: position ? fromX : undefined,
15476
+ y: position ? fromY : undefined,
15477
+ ...routeEvidence,
15478
+ reason: "expected_route_not_reached",
15479
+ };
15480
+ }
15249
15481
  return {
15250
15482
  ...base,
15251
15483
  ...setupScopeEvidence(scope),
@@ -15261,6 +15493,24 @@ async function executeSetupAction(action, ordinal, viewport) {
15261
15493
  coordinate_mode: mode,
15262
15494
  x: position ? fromX : undefined,
15263
15495
  y: position ? fromY : undefined,
15496
+ ...routeEvidence,
15497
+ };
15498
+ }
15499
+ const routeEvidence = await waitForSetupActionRoute(action, timeout);
15500
+ if (routeEvidence && !routeEvidence.route_matched) {
15501
+ return {
15502
+ ...base,
15503
+ ...setupScopeEvidence(scope),
15504
+ count,
15505
+ target_index: targetIndex,
15506
+ text: matchedText,
15507
+ force: action.force === true || undefined,
15508
+ click_count: clickCount > 1 ? clickCount : undefined,
15509
+ coordinate_mode: mode,
15510
+ x: position ? fromX : undefined,
15511
+ y: position ? fromY : undefined,
15512
+ ...routeEvidence,
15513
+ reason: "expected_route_not_reached",
15264
15514
  };
15265
15515
  }
15266
15516
  return {
@@ -15275,6 +15525,7 @@ async function executeSetupAction(action, ordinal, viewport) {
15275
15525
  coordinate_mode: mode,
15276
15526
  x: position ? fromX : undefined,
15277
15527
  y: position ? fromY : undefined,
15528
+ ...routeEvidence,
15278
15529
  };
15279
15530
  }
15280
15531
  if (type === "fill" || type === "set_input_value") {
@@ -17232,7 +17483,7 @@ function usage() {
17232
17483
  " riddle-proof-loop regression-pack run [--pack oc-flow-regression|--pack-file <file>] [--local-core true|false; default true] [--hosted-riddle true|false; default false] [--format json|markdown|compact-json; default json] [--output <dir>|--output-dir <dir>]",
17233
17484
  " riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
17234
17485
  " riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
17235
- " riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static]",
17486
+ " riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static] [--quiet]",
17236
17487
  " riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
17237
17488
  " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
17238
17489
  " riddle-proof-loop riddle-poll <job-id> [--wait] [--attempts n] [--quiet]",
@@ -18001,6 +18252,18 @@ function formatPollDuration(ms) {
18001
18252
  const remainder = seconds % 60;
18002
18253
  return minutes > 0 ? `${minutes}m${String(remainder).padStart(2, "0")}s` : `${seconds}s`;
18003
18254
  }
18255
+ function formatByteCount(bytes) {
18256
+ if (typeof bytes !== "number" || !Number.isFinite(bytes)) return "n/a";
18257
+ if (bytes < 1024) return `${bytes}B`;
18258
+ const units = ["KB", "MB", "GB", "TB"];
18259
+ let value = bytes / 1024;
18260
+ let unitIndex = 0;
18261
+ while (value >= 1024 && unitIndex < units.length - 1) {
18262
+ value /= 1024;
18263
+ unitIndex += 1;
18264
+ }
18265
+ return `${value >= 10 ? value.toFixed(0) : value.toFixed(1)}${units[unitIndex]}`;
18266
+ }
18004
18267
  function riddlePollProgressLine(snapshot) {
18005
18268
  const submittedAt = snapshot.submitted_at || "not-submitted";
18006
18269
  const queuePart = snapshot.running_without_submission ? ` waiting_for_submit=${formatPollDuration(snapshot.pre_submission_elapsed_ms)}${snapshot.queue_elapsed_ms !== null ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
@@ -18014,6 +18277,18 @@ function riddlePollProgressLine(snapshot) {
18014
18277
  `submitted_at=${submittedAt}${queuePart}${terminalPart}`
18015
18278
  ].join(" ");
18016
18279
  }
18280
+ function riddlePreviewProgressLine(snapshot) {
18281
+ const idPart = snapshot.id ? ` id=${snapshot.id}` : "";
18282
+ const statusPart = snapshot.status ? ` status=${snapshot.status}` : "";
18283
+ const attemptPart = snapshot.attempt && snapshot.attempts ? ` attempt=${snapshot.attempt}/${snapshot.attempts}` : "";
18284
+ const previewPart = snapshot.preview_url ? ` url=${snapshot.preview_url}` : "";
18285
+ const filePart = typeof snapshot.file_count === "number" ? ` files=${snapshot.file_count}` : "";
18286
+ const totalPart = typeof snapshot.total_bytes === "number" ? ` bytes=${formatByteCount(snapshot.total_bytes)}` : "";
18287
+ const archivePart = typeof snapshot.tarball_bytes === "number" ? ` archive=${formatByteCount(snapshot.tarball_bytes)}` : "";
18288
+ const recoveryPart = snapshot.publish_error ? ` publish_error=${JSON.stringify(snapshot.publish_error)}` : "";
18289
+ const messagePart = snapshot.message ? ` ${snapshot.message}` : "";
18290
+ return `[riddle-preview] ${snapshot.stage}${idPart}${statusPart}${attemptPart} elapsed=${formatPollDuration(snapshot.elapsed_ms)} label=${snapshot.label} framework=${snapshot.framework}${filePart}${totalPart}${archivePart}${previewPart}${recoveryPart}${messagePart}`;
18291
+ }
18017
18292
  function readJsonValue(value, label) {
18018
18293
  if (!value) throw new Error(`${label} is required.`);
18019
18294
  const raw = value === "-" ? readStdin() : (0, import_node_fs6.existsSync)(value) ? (0, import_node_fs6.readFileSync)(value, "utf-8") : value;
@@ -21689,7 +21964,14 @@ async function main() {
21689
21964
  if (command === "riddle-preview-deploy") {
21690
21965
  const buildDir = positional[1];
21691
21966
  const label = positional[2];
21692
- const result = await createRiddleApiClient(riddleClientConfig(options)).deployPreview(buildDir, label, previewFrameworkOption(options));
21967
+ const clientConfig = riddleClientConfig(options);
21968
+ if (options.quiet !== true) {
21969
+ clientConfig.onPreviewProgress = (snapshot) => {
21970
+ process.stderr.write(`${riddlePreviewProgressLine(snapshot)}
21971
+ `);
21972
+ };
21973
+ }
21974
+ const result = await createRiddleApiClient(clientConfig).deployPreview(buildDir, label, previewFrameworkOption(options));
21693
21975
  for (const warning of result.warnings ?? []) {
21694
21976
  process.stderr.write(`Warning: ${warning}
21695
21977
  `);
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-UGHN77PS.js";
3
- import "./chunk-PEWAIEER.js";
4
- import "./chunk-TWTEUS7R.js";
2
+ import "./chunk-F4HKK2YH.js";
3
+ import "./chunk-Z2LCVROU.js";
4
+ import "./chunk-DI2XNGEZ.js";
5
5
  import "./chunk-ZREWMTFA.js";
6
6
  import "./chunk-ZQWVXQKJ.js";
7
7
  import "./chunk-RDPG554T.js";