@riddledc/riddle-proof 0.7.26 → 0.7.28

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
@@ -6895,6 +6895,9 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
6895
6895
  ];
6896
6896
  var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
6897
6897
  "click",
6898
+ "fill",
6899
+ "set_input_value",
6900
+ "local_storage",
6898
6901
  "wait",
6899
6902
  "wait_for_selector",
6900
6903
  "wait_for_text"
@@ -6908,6 +6911,18 @@ function isRecord(value) {
6908
6911
  function stringValue2(value) {
6909
6912
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
6910
6913
  }
6914
+ function hasOwn(value, key) {
6915
+ return Object.prototype.hasOwnProperty.call(value, key);
6916
+ }
6917
+ function stringFromOwn(input, ...keys) {
6918
+ for (const key of keys) {
6919
+ if (hasOwn(input, key)) {
6920
+ const value = input[key];
6921
+ if (value !== void 0 && value !== null) return String(value);
6922
+ }
6923
+ }
6924
+ return void 0;
6925
+ }
6911
6926
  function numberValue(value) {
6912
6927
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
6913
6928
  }
@@ -6992,6 +7007,11 @@ function jsonRecord(value) {
6992
7007
  if (!isRecord(value)) return void 0;
6993
7008
  return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
6994
7009
  }
7010
+ function stringRecord(value) {
7011
+ if (!isRecord(value)) return void 0;
7012
+ const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
7013
+ return entries.length ? Object.fromEntries(entries) : void 0;
7014
+ }
6995
7015
  function toJsonValue(value) {
6996
7016
  if (value === null || value === void 0) return null;
6997
7017
  if (typeof value === "string" || typeof value === "boolean") return value;
@@ -7039,15 +7059,30 @@ function normalizeSetupAction(input, index) {
7039
7059
  if (!isRecord(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
7040
7060
  const type = normalizeSetupActionType(stringValue2(input.type), index);
7041
7061
  const selector = stringValue2(input.selector);
7042
- if ((type === "click" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
7062
+ if ((type === "click" || type === "fill" || type === "set_input_value" || type === "wait_for_selector" || type === "wait_for_text") && !selector) {
7043
7063
  throw new Error(`target.setup_actions[${index}] ${type} requires selector.`);
7044
7064
  }
7045
7065
  if (type === "wait_for_text" && !stringValue2(input.text) && !stringValue2(input.pattern)) {
7046
7066
  throw new Error(`target.setup_actions[${index}] wait_for_text requires text or pattern.`);
7047
7067
  }
7068
+ const value = stringFromOwn(input, "value", "input_value", "inputValue");
7069
+ const hasJsonValue = hasOwn(input, "value_json") || hasOwn(input, "valueJson") || hasOwn(input, "json");
7070
+ if ((type === "fill" || type === "set_input_value") && value === void 0 && !hasJsonValue) {
7071
+ throw new Error(`target.setup_actions[${index}] ${type} requires value.`);
7072
+ }
7073
+ const key = stringValue2(input.key);
7074
+ if (type === "local_storage" && !key) {
7075
+ throw new Error(`target.setup_actions[${index}] local_storage requires key.`);
7076
+ }
7077
+ if (type === "local_storage" && value === void 0 && !hasJsonValue) {
7078
+ throw new Error(`target.setup_actions[${index}] local_storage requires value.`);
7079
+ }
7048
7080
  return {
7049
7081
  type,
7050
7082
  selector,
7083
+ key,
7084
+ value,
7085
+ value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
7051
7086
  text: stringValue2(input.text),
7052
7087
  pattern: stringValue2(input.pattern),
7053
7088
  flags: stringValue2(input.flags),
@@ -7055,6 +7090,7 @@ function normalizeSetupAction(input, index) {
7055
7090
  ms: numberValue(input.ms) ?? numberValue(input.wait_ms) ?? numberValue(input.waitMs),
7056
7091
  timeout_ms: numberValue(input.timeout_ms) ?? numberValue(input.timeoutMs),
7057
7092
  after_ms: numberValue(input.after_ms) ?? numberValue(input.afterMs),
7093
+ reload: input.reload === true,
7058
7094
  continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
7059
7095
  };
7060
7096
  }
@@ -7063,6 +7099,33 @@ function normalizeSetupActions(value) {
7063
7099
  if (!Array.isArray(value)) throw new Error("target.setup_actions must be an array.");
7064
7100
  return value.map(normalizeSetupAction);
7065
7101
  }
7102
+ function normalizeNetworkMock(input, index) {
7103
+ if (!isRecord(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
7104
+ const url = stringValue2(input.url) || stringValue2(input.glob) || stringValue2(input.pattern);
7105
+ if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
7106
+ const status = numberValue(input.status) ?? 200;
7107
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
7108
+ throw new Error(`target.network_mocks[${index}].status must be an HTTP status code.`);
7109
+ }
7110
+ const body = stringValue2(input.body) ?? stringValue2(input.body_text) ?? stringValue2(input.bodyText);
7111
+ const hasJsonBody = Object.prototype.hasOwnProperty.call(input, "body_json") || Object.prototype.hasOwnProperty.call(input, "bodyJson") || Object.prototype.hasOwnProperty.call(input, "json");
7112
+ return {
7113
+ label: normalizeName(input.label || input.name, `network-mock-${index + 1}`),
7114
+ url,
7115
+ method: stringValue2(input.method)?.toUpperCase(),
7116
+ status,
7117
+ content_type: stringValue2(input.content_type) || stringValue2(input.contentType),
7118
+ headers: stringRecord(input.headers),
7119
+ body,
7120
+ body_json: hasJsonBody ? toJsonValue(input.body_json ?? input.bodyJson ?? input.json) : void 0,
7121
+ required: input.required === false ? false : true
7122
+ };
7123
+ }
7124
+ function normalizeNetworkMocks(value) {
7125
+ if (value === void 0) return void 0;
7126
+ if (!Array.isArray(value)) throw new Error("target.network_mocks must be an array.");
7127
+ return value.map(normalizeNetworkMock);
7128
+ }
7066
7129
  function normalizeCheck(input, index) {
7067
7130
  if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
7068
7131
  const type = stringValue2(input.type);
@@ -7132,7 +7195,8 @@ function normalizeRiddleProofProfile(input, options = {}) {
7132
7195
  timeout_sec: timeoutSecValue(targetInput.timeout_sec) ?? timeoutSecValue(targetInput.timeoutSec) ?? timeoutSecValue(targetInput.riddle_timeout_sec) ?? timeoutSecValue(targetInput.riddleTimeoutSec),
7133
7196
  wait_for_selector: stringValue2(targetInput.wait_for_selector) || stringValue2(targetInput.waitForSelector),
7134
7197
  wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
7135
- setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions)
7198
+ setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions),
7199
+ network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
7136
7200
  },
7137
7201
  checks,
7138
7202
  artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
@@ -7404,6 +7468,50 @@ function assessSetupActionsFromEvidence(profile, evidence) {
7404
7468
  message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
7405
7469
  };
7406
7470
  }
7471
+ function assessNetworkMocksFromEvidence(profile, evidence) {
7472
+ const mocks = profile.target.network_mocks || [];
7473
+ if (!mocks.length) return void 0;
7474
+ const events = evidence.network_mocks || [];
7475
+ const failed = [];
7476
+ const requiredMocks = mocks.filter((mock) => mock.required !== false);
7477
+ for (const mock of requiredMocks) {
7478
+ const hits = events.filter((event) => event.label === mock.label && event.ok !== false).length;
7479
+ if (hits < 1) {
7480
+ failed.push({
7481
+ label: mock.label,
7482
+ url: mock.url,
7483
+ method: mock.method || null,
7484
+ reason: "required_mock_not_hit"
7485
+ });
7486
+ }
7487
+ }
7488
+ for (const event of events) {
7489
+ if (event.ok === false) {
7490
+ failed.push({
7491
+ label: event.label ?? null,
7492
+ url: event.url ?? null,
7493
+ method: event.method ?? null,
7494
+ reason: event.reason ?? event.error ?? "mock_failed"
7495
+ });
7496
+ }
7497
+ }
7498
+ return {
7499
+ type: "network_mocks_succeeded",
7500
+ label: "network mocks succeeded",
7501
+ status: failed.length ? "failed" : "passed",
7502
+ evidence: {
7503
+ mock_count: mocks.length,
7504
+ required_count: requiredMocks.length,
7505
+ hit_count: events.filter((event) => event.ok !== false).length,
7506
+ hits_by_label: Object.fromEntries(mocks.map((mock) => [
7507
+ mock.label,
7508
+ events.filter((event) => event.label === mock.label && event.ok !== false).length
7509
+ ])),
7510
+ failed
7511
+ },
7512
+ message: failed.length ? `Network mocks failed or were not hit for ${failed.length} mock(s).` : void 0
7513
+ };
7514
+ }
7407
7515
  function profileStatusFromEvidence(profile, evidence, checks) {
7408
7516
  if (!evidence) return "proof_insufficient";
7409
7517
  const viewports = evidence.viewports || [];
@@ -7418,6 +7526,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
7418
7526
  function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
7419
7527
  const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
7420
7528
  const checks = evidence ? [
7529
+ assessNetworkMocksFromEvidence(profile, evidence),
7421
7530
  assessSetupActionsFromEvidence(profile, evidence),
7422
7531
  ...profile.checks.map((check) => assessCheckFromEvidence(check, evidence))
7423
7532
  ].filter((check) => Boolean(check)) : [];
@@ -7618,6 +7727,49 @@ function horizontalBoundsOverflowPx(value) {
7618
7727
  function assessProfile(profile, evidence) {
7619
7728
  const checks = [];
7620
7729
  const viewports = evidence.viewports || [];
7730
+ if (profile.target && Array.isArray(profile.target.network_mocks) && profile.target.network_mocks.length) {
7731
+ const events = evidence.network_mocks || [];
7732
+ const requiredMocks = profile.target.network_mocks.filter((mock) => mock && mock.required !== false);
7733
+ const failed = [];
7734
+ for (const mock of requiredMocks) {
7735
+ const hits = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
7736
+ if (hits < 1) {
7737
+ failed.push({
7738
+ label: mock.label,
7739
+ url: mock.url,
7740
+ method: mock.method || null,
7741
+ reason: "required_mock_not_hit",
7742
+ });
7743
+ }
7744
+ }
7745
+ for (const event of events) {
7746
+ if (event && event.ok === false) {
7747
+ failed.push({
7748
+ label: event.label || null,
7749
+ url: event.url || null,
7750
+ method: event.method || null,
7751
+ reason: event.reason || event.error || "mock_failed",
7752
+ });
7753
+ }
7754
+ }
7755
+ const hitsByLabel = {};
7756
+ for (const mock of profile.target.network_mocks) {
7757
+ hitsByLabel[mock.label] = events.filter((event) => event && event.label === mock.label && event.ok !== false).length;
7758
+ }
7759
+ checks.push({
7760
+ type: "network_mocks_succeeded",
7761
+ label: "network mocks succeeded",
7762
+ status: failed.length ? "failed" : "passed",
7763
+ evidence: {
7764
+ mock_count: profile.target.network_mocks.length,
7765
+ required_count: requiredMocks.length,
7766
+ hit_count: events.filter((event) => event && event.ok !== false).length,
7767
+ hits_by_label: hitsByLabel,
7768
+ failed,
7769
+ },
7770
+ message: failed.length ? "Network mocks failed or were not hit for " + failed.length + " mock(s)." : undefined,
7771
+ });
7772
+ }
7621
7773
  if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
7622
7774
  const actionCount = profile.target.setup_actions.length;
7623
7775
  const failed = [];
@@ -7807,6 +7959,7 @@ const profileSlug = ${serializableSlug};
7807
7959
  const capturedAt = new Date().toISOString();
7808
7960
  const consoleEvents = [];
7809
7961
  const pageErrors = [];
7962
+ const networkMockEvents = [];
7810
7963
  page.on("console", (message) => {
7811
7964
  const type = message.type();
7812
7965
  if (type === "error" || type === "warning" || type === "assert") {
@@ -7842,6 +7995,14 @@ function setupTextMatches(sample, action) {
7842
7995
  }
7843
7996
  return String(sample || "").includes(action.text || "");
7844
7997
  }
7998
+ function setupHasOwn(action, key) {
7999
+ return Boolean(action) && Object.keys(action).includes(key);
8000
+ }
8001
+ function setupActionValue(action) {
8002
+ if (setupHasOwn(action, "value_json")) return JSON.stringify(action.value_json);
8003
+ if (setupHasOwn(action, "value")) return String(action.value ?? "");
8004
+ return "";
8005
+ }
7845
8006
  async function setupLocatorText(locator, index) {
7846
8007
  return await locator.nth(index).textContent({ timeout: 1000 }).catch(() => "");
7847
8008
  }
@@ -7853,6 +8014,49 @@ function compactSetupResultText(value) {
7853
8014
  async function setupLocatorVisible(locator, index) {
7854
8015
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
7855
8016
  }
8017
+ async function registerNetworkMocks(mocks) {
8018
+ for (const mock of mocks || []) {
8019
+ await page.route(mock.url, async (route) => {
8020
+ const request = route.request();
8021
+ const method = request.method ? request.method() : "";
8022
+ if (mock.method && method.toUpperCase() !== String(mock.method).toUpperCase()) {
8023
+ await route.continue();
8024
+ return;
8025
+ }
8026
+ try {
8027
+ const headers = { ...(mock.headers || {}) };
8028
+ let body = mock.body || "";
8029
+ let contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "text/plain";
8030
+ if (mock.body_json !== undefined) {
8031
+ body = JSON.stringify(mock.body_json);
8032
+ contentType = mock.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
8033
+ }
8034
+ networkMockEvents.push({
8035
+ ok: true,
8036
+ label: mock.label,
8037
+ url: request.url(),
8038
+ method,
8039
+ status: mock.status || 200,
8040
+ });
8041
+ await route.fulfill({
8042
+ status: mock.status || 200,
8043
+ headers,
8044
+ contentType,
8045
+ body,
8046
+ });
8047
+ } catch (error) {
8048
+ networkMockEvents.push({
8049
+ ok: false,
8050
+ label: mock.label,
8051
+ url: request.url(),
8052
+ method,
8053
+ error: String(error && error.message ? error.message : error).slice(0, 1000),
8054
+ });
8055
+ throw error;
8056
+ }
8057
+ });
8058
+ }
8059
+ }
7856
8060
  async function executeSetupAction(action, ordinal) {
7857
8061
  const type = setupActionType(action);
7858
8062
  const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null };
@@ -7867,6 +8071,16 @@ async function executeSetupAction(action, ordinal) {
7867
8071
  await page.waitForSelector(action.selector, { state: "visible", timeout });
7868
8072
  return { ...base, ok: true, timeout_ms: timeout };
7869
8073
  }
8074
+ if (type === "local_storage") {
8075
+ const value = setupActionValue(action);
8076
+ await page.evaluate(({ key, value }) => {
8077
+ window.localStorage.setItem(key, value);
8078
+ }, { key: action.key, value });
8079
+ if (action.reload === true) {
8080
+ await page.reload({ waitUntil: "domcontentloaded", timeout: 45000 });
8081
+ }
8082
+ return { ...base, ok: true, key: action.key, value_length: value.length, reload: action.reload === true };
8083
+ }
7870
8084
  if (type === "click") {
7871
8085
  const locator = page.locator(action.selector);
7872
8086
  const count = await locator.count();
@@ -7899,6 +8113,16 @@ async function executeSetupAction(action, ordinal) {
7899
8113
  await locator.nth(targetIndex).click({ timeout });
7900
8114
  return { ...base, ok: true, count, target_index: targetIndex, text: matchedText };
7901
8115
  }
8116
+ if (type === "fill" || type === "set_input_value") {
8117
+ const locator = page.locator(action.selector);
8118
+ const count = await locator.count();
8119
+ if (!count) return { ...base, reason: "selector_not_found", count };
8120
+ const targetIndex = Number.isInteger(action.index) ? action.index : 0;
8121
+ if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex };
8122
+ const value = setupActionValue(action);
8123
+ await locator.nth(targetIndex).fill(value, { timeout });
8124
+ return { ...base, ok: true, count, target_index: targetIndex, value_length: value.length };
8125
+ }
7902
8126
  if (type === "wait_for_text") {
7903
8127
  const locator = page.locator(action.selector);
7904
8128
  const startedAt = Date.now();
@@ -8114,6 +8338,7 @@ function buildProfileEvidence(currentViewports) {
8114
8338
  fatal_count: consoleEvents.filter((event) => event.type === "error" || event.type === "assert").length,
8115
8339
  },
8116
8340
  page_errors: pageErrors,
8341
+ network_mocks: networkMockEvents.slice(),
8117
8342
  dom_summary: {
8118
8343
  expected_viewport_count: expectedViewportCount,
8119
8344
  viewport_count: currentViewports.length,
@@ -8123,6 +8348,8 @@ function buildProfileEvidence(currentViewports) {
8123
8348
  overflow_px: currentViewports.map((viewport) => viewport.overflow_px),
8124
8349
  bounds_overflow_px: currentViewports.map((viewport) => viewport.bounds_overflow_px),
8125
8350
  overflow_offender_counts: currentViewports.map((viewport) => (viewport.overflow_offenders || []).length),
8351
+ network_mock_count: (profile.target.network_mocks || []).length,
8352
+ network_mock_hit_count: networkMockEvents.filter((event) => event.ok !== false).length,
8126
8353
  },
8127
8354
  };
8128
8355
  }
@@ -8136,6 +8363,9 @@ async function saveProfileArtifacts(currentViewports) {
8136
8363
  }
8137
8364
  return result;
8138
8365
  }
8366
+ await registerNetworkMocks(profile.target.network_mocks || []);
8367
+ consoleEvents.length = 0;
8368
+ pageErrors.length = 0;
8139
8369
  let result = await saveProfileArtifacts(viewports);
8140
8370
  for (const viewport of profile.target.viewports || []) {
8141
8371
  viewports.push(await captureViewport(viewport));
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-SDSBS64X.js";
13
+ } from "./chunk-NUGGW7NX.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport