@riddledc/riddle-proof 0.7.127 → 0.7.129

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/index.js CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  resolveRiddleProofProfileTimeoutSec,
63
63
  slugifyRiddleProofProfileName,
64
64
  summarizeRiddleProofProfileResult
65
- } from "./chunk-JLINSUKO.js";
65
+ } from "./chunk-TDK6WFH3.js";
66
66
  import {
67
67
  DEFAULT_RIDDLE_API_BASE_URL,
68
68
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -291,6 +291,37 @@ function jsonValueType(value) {
291
291
  if (typeof value === "string") return "string";
292
292
  return "object";
293
293
  }
294
+ function compactJsonAssertionSample(value, depth = 0) {
295
+ if (typeof value === "string") return value.length > 240 ? `${value.slice(0, 237)}...` : value;
296
+ if (value === null || typeof value === "boolean" || typeof value === "number") return toJsonValue(value);
297
+ if (Array.isArray(value)) {
298
+ if (depth >= 2) return `[array:${value.length}]`;
299
+ return value.slice(0, 3).map((item) => compactJsonAssertionSample(item, depth + 1));
300
+ }
301
+ if (isRecord(value)) {
302
+ const entries = Object.entries(value).slice(0, 8);
303
+ if (depth >= 2) return `[object:${Object.keys(value).length} keys]`;
304
+ return Object.fromEntries(entries.map(([key, child]) => [key, compactJsonAssertionSample(child, depth + 1)]));
305
+ }
306
+ return String(value);
307
+ }
308
+ function attachJsonAssertionObservedValue(result, value) {
309
+ const type = jsonValueType(value);
310
+ if (type === "array" && Array.isArray(value)) {
311
+ result.observed_length = value.length;
312
+ result.observed_omitted_count = Math.max(0, value.length - 3);
313
+ result.observed_sample = compactJsonAssertionSample(value);
314
+ return;
315
+ }
316
+ if (type === "object" && isRecord(value)) {
317
+ const keyCount = Object.keys(value).length;
318
+ result.observed_key_count = keyCount;
319
+ result.observed_omitted_count = Math.max(0, keyCount - 8);
320
+ result.observed_sample = compactJsonAssertionSample(value);
321
+ return;
322
+ }
323
+ result.observed = toJsonValue(value);
324
+ }
294
325
  function deepJsonEqual(left, right) {
295
326
  if (left === right) return true;
296
327
  if (typeof left !== typeof right) return false;
@@ -388,7 +419,7 @@ function evaluateHttpStatusBodyJsonAssertion(root, assertion) {
388
419
  exists: resolved.exists,
389
420
  observed_type: resolved.exists ? jsonValueType(resolved.value) : "missing"
390
421
  };
391
- if (resolved.exists) result.observed = toJsonValue(resolved.value);
422
+ if (resolved.exists) attachJsonAssertionObservedValue(result, resolved.value);
392
423
  if (resolved.error) errors.push(resolved.error);
393
424
  if (hasOwn(assertion, "exists")) {
394
425
  result.expected_exists = assertion.exists;
@@ -1481,6 +1512,10 @@ function httpStatusBodyJsonAssertionFailures(result, check) {
1481
1512
  ok: false,
1482
1513
  exists: assertion.exists === true,
1483
1514
  observed: hasOwn(assertion, "observed") ? toJsonValue(assertion.observed) : void 0,
1515
+ observed_sample: hasOwn(assertion, "observed_sample") ? toJsonValue(assertion.observed_sample) : void 0,
1516
+ observed_length: numberValue(assertion.observed_length),
1517
+ observed_key_count: numberValue(assertion.observed_key_count),
1518
+ observed_omitted_count: numberValue(assertion.observed_omitted_count),
1484
1519
  observed_type: stringValue(assertion.observed_type) || "missing",
1485
1520
  expected_exists: booleanValue(assertion.expected_exists),
1486
1521
  equals: hasOwn(assertion, "equals") ? toJsonValue(assertion.equals) : void 0,
@@ -1866,6 +1901,49 @@ function matchText(sample, check) {
1866
1901
  }
1867
1902
  return sample.includes(check.text || "");
1868
1903
  }
1904
+ function compactTextEvidenceSample(value) {
1905
+ return String(value || "").replace(/\s+/g, " ").trim();
1906
+ }
1907
+ function textSampleAroundMatch(sample, index, length) {
1908
+ if (index < 0) return void 0;
1909
+ const source = String(sample || "");
1910
+ const context = 120;
1911
+ const start = Math.max(0, index - context);
1912
+ const end = Math.min(source.length, index + Math.max(length, 1) + context);
1913
+ const prefix = start > 0 ? "..." : "";
1914
+ const suffix = end < source.length ? "..." : "";
1915
+ const compacted = compactTextEvidenceSample(`${prefix}${source.slice(start, end)}${suffix}`);
1916
+ return compacted ? compacted.slice(0, 240) : void 0;
1917
+ }
1918
+ function textMatchSamples(sample, check) {
1919
+ const source = String(sample || "");
1920
+ if (!source) return [];
1921
+ if (check.pattern) {
1922
+ try {
1923
+ const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
1924
+ const match = new RegExp(check.pattern, flags).exec(source);
1925
+ const sampleText2 = match ? textSampleAroundMatch(source, match.index, match[0]?.length || 1) : void 0;
1926
+ return sampleText2 ? [sampleText2] : [];
1927
+ } catch {
1928
+ return [];
1929
+ }
1930
+ }
1931
+ const text = check.text || "";
1932
+ if (!text) return [];
1933
+ const index = source.indexOf(text);
1934
+ const sampleText = textSampleAroundMatch(source, index, text.length);
1935
+ return sampleText ? [sampleText] : [];
1936
+ }
1937
+ function textCheckFailureSamples(viewport, check) {
1938
+ const key = textKey(check);
1939
+ const captured = viewport.text_match_samples?.[key] || [];
1940
+ const capturedSamples = captured.map((sample) => compactTextEvidenceSample(sample).slice(0, 240)).filter(Boolean);
1941
+ if (capturedSamples.length) return capturedSamples.slice(0, 3);
1942
+ const matchedSamples = textMatchSamples(viewport.body_text_sample || "", check);
1943
+ if (matchedSamples.length) return matchedSamples.slice(0, 3);
1944
+ const fallback = compactTextEvidenceSample(viewport.body_text_sample || "").slice(0, 240);
1945
+ return fallback ? [fallback] : [];
1946
+ }
1869
1947
  function allowedMessageSample(input) {
1870
1948
  if (!isRecord(input)) return String(input || "");
1871
1949
  const parts = [
@@ -2310,10 +2388,17 @@ function assessCheckFromEvidence(check, evidence) {
2310
2388
  if (check.type === "text_visible" || check.type === "text_absent") {
2311
2389
  const key = textKey(check);
2312
2390
  const expectedVisible = check.type === "text_visible";
2313
- const matches = viewports.map((viewport) => {
2391
+ const results = viewports.map((viewport) => {
2314
2392
  const fromEvidence = viewport.text_matches?.[key];
2315
- return typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
2393
+ const matched = typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
2394
+ const failedAgainstExpectation = matched !== expectedVisible;
2395
+ return {
2396
+ viewport: viewport.name,
2397
+ matched,
2398
+ samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : []
2399
+ };
2316
2400
  });
2401
+ const matches = results.map((result) => result.matched);
2317
2402
  const failed = matches.filter((matched) => matched !== expectedVisible).length;
2318
2403
  return {
2319
2404
  type: check.type,
@@ -2322,7 +2407,8 @@ function assessCheckFromEvidence(check, evidence) {
2322
2407
  evidence: {
2323
2408
  text: check.text || null,
2324
2409
  pattern: check.pattern || null,
2325
- matches
2410
+ matches,
2411
+ viewports: results.map((result) => toJsonValue(result))
2326
2412
  },
2327
2413
  message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
2328
2414
  };
@@ -2909,6 +2995,48 @@ function textMatches(sample, check) {
2909
2995
  }
2910
2996
  return String(sample || "").includes(check.text || "");
2911
2997
  }
2998
+ function compactTextEvidenceSample(value) {
2999
+ return String(value || "").replace(/\s+/g, " ").trim();
3000
+ }
3001
+ function textSampleAroundMatch(sample, index, length) {
3002
+ if (index < 0) return undefined;
3003
+ const source = String(sample || "");
3004
+ const context = 120;
3005
+ const start = Math.max(0, index - context);
3006
+ const end = Math.min(source.length, index + Math.max(length, 1) + context);
3007
+ const prefix = start > 0 ? "..." : "";
3008
+ const suffix = end < source.length ? "..." : "";
3009
+ const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
3010
+ return compacted ? compacted.slice(0, 240) : undefined;
3011
+ }
3012
+ function textMatchSamples(sample, check) {
3013
+ const source = String(sample || "");
3014
+ if (!source) return [];
3015
+ if (check.pattern) {
3016
+ try {
3017
+ const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
3018
+ const match = new RegExp(check.pattern, flags).exec(source);
3019
+ const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
3020
+ return sampleText ? [sampleText] : [];
3021
+ } catch { return []; }
3022
+ }
3023
+ const text = check.text || "";
3024
+ if (!text) return [];
3025
+ const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
3026
+ return sampleText ? [sampleText] : [];
3027
+ }
3028
+ function textCheckFailureSamples(viewport, check) {
3029
+ const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
3030
+ const captured = viewport && viewport.text_match_samples && Array.isArray(viewport.text_match_samples[key]) ? viewport.text_match_samples[key] : [];
3031
+ const capturedSamples = captured
3032
+ .map((sample) => compactTextEvidenceSample(sample).slice(0, 240))
3033
+ .filter(Boolean);
3034
+ if (capturedSamples.length) return capturedSamples.slice(0, 3);
3035
+ const matchedSamples = textMatchSamples(viewport && viewport.body_text_sample || "", check);
3036
+ if (matchedSamples.length) return matchedSamples.slice(0, 3);
3037
+ const fallback = compactTextEvidenceSample(viewport && viewport.body_text_sample || "").slice(0, 240);
3038
+ return fallback ? [fallback] : [];
3039
+ }
2912
3040
  function allowedMessageSample(input) {
2913
3041
  if (!input || typeof input !== "object" || Array.isArray(input)) return String(input || "");
2914
3042
  const parts = [
@@ -3111,6 +3239,10 @@ function httpStatusBodyJsonAssertionFailures(result, check) {
3111
3239
  ok: false,
3112
3240
  exists: assertion.exists === true,
3113
3241
  observed: Object.hasOwn(assertion, "observed") ? assertion.observed : undefined,
3242
+ observed_sample: Object.hasOwn(assertion, "observed_sample") ? assertion.observed_sample : undefined,
3243
+ observed_length: typeof assertion.observed_length === "number" && Number.isFinite(assertion.observed_length) ? assertion.observed_length : undefined,
3244
+ observed_key_count: typeof assertion.observed_key_count === "number" && Number.isFinite(assertion.observed_key_count) ? assertion.observed_key_count : undefined,
3245
+ observed_omitted_count: typeof assertion.observed_omitted_count === "number" && Number.isFinite(assertion.observed_omitted_count) ? assertion.observed_omitted_count : undefined,
3114
3246
  observed_type: typeof assertion.observed_type === "string" && assertion.observed_type ? assertion.observed_type : "missing",
3115
3247
  expected_exists: typeof assertion.expected_exists === "boolean" ? assertion.expected_exists : undefined,
3116
3248
  equals: Object.hasOwn(assertion, "equals") ? assertion.equals : undefined,
@@ -3982,13 +4114,22 @@ function assessProfile(profile, evidence) {
3982
4114
  if (check.type === "text_visible" || check.type === "text_absent") {
3983
4115
  const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
3984
4116
  const expectedVisible = check.type === "text_visible";
3985
- const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
4117
+ const results = checkViewports.map((viewport) => {
4118
+ const matched = viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check);
4119
+ const failedAgainstExpectation = matched !== expectedVisible;
4120
+ return {
4121
+ viewport: viewport.name,
4122
+ matched,
4123
+ samples: failedAgainstExpectation ? textCheckFailureSamples(viewport, check) : [],
4124
+ };
4125
+ });
4126
+ const matches = results.map((result) => result.matched);
3986
4127
  const failed = matches.filter((matched) => matched !== expectedVisible).length;
3987
4128
  checks.push({
3988
4129
  type: check.type,
3989
4130
  label: check.label || check.type,
3990
4131
  status: failed ? "failed" : "passed",
3991
- evidence: { text: check.text, pattern: check.pattern, matches },
4132
+ evidence: { text: check.text, pattern: check.pattern, matches, viewports: results },
3992
4133
  message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
3993
4134
  });
3994
4135
  continue;
@@ -4308,6 +4449,36 @@ function textMatches(sample, check) {
4308
4449
  }
4309
4450
  return String(sample || "").includes(check.text || "");
4310
4451
  }
4452
+ function compactTextEvidenceSample(value) {
4453
+ return String(value || "").replace(/\s+/g, " ").trim();
4454
+ }
4455
+ function textSampleAroundMatch(sample, index, length) {
4456
+ if (index < 0) return undefined;
4457
+ const source = String(sample || "");
4458
+ const context = 120;
4459
+ const start = Math.max(0, index - context);
4460
+ const end = Math.min(source.length, index + Math.max(length, 1) + context);
4461
+ const prefix = start > 0 ? "..." : "";
4462
+ const suffix = end < source.length ? "..." : "";
4463
+ const compacted = compactTextEvidenceSample(prefix + source.slice(start, end) + suffix);
4464
+ return compacted ? compacted.slice(0, 240) : undefined;
4465
+ }
4466
+ function textMatchSamples(sample, check) {
4467
+ const source = String(sample || "");
4468
+ if (!source) return [];
4469
+ if (check.pattern) {
4470
+ try {
4471
+ const flags = Array.from(new Set(String(check.flags || "").replace(/[gy]/g, "").split(""))).join("");
4472
+ const match = new RegExp(check.pattern, flags).exec(source);
4473
+ const sampleText = match ? textSampleAroundMatch(source, match.index, match[0] ? match[0].length : 1) : undefined;
4474
+ return sampleText ? [sampleText] : [];
4475
+ } catch { return []; }
4476
+ }
4477
+ const text = check.text || "";
4478
+ if (!text) return [];
4479
+ const sampleText = textSampleAroundMatch(source, source.indexOf(text), text.length);
4480
+ return sampleText ? [sampleText] : [];
4481
+ }
4311
4482
  function profileCheckAppliesToViewport(check, viewport) {
4312
4483
  if (!Array.isArray(check.viewports) || !check.viewports.length) return true;
4313
4484
  return Boolean(viewport && viewport.name && check.viewports.includes(viewport.name));
@@ -5318,6 +5489,38 @@ function jsonProbeValueType(value) {
5318
5489
  if (typeof value === "string") return "string";
5319
5490
  return "object";
5320
5491
  }
5492
+ function compactJsonProbeSample(value, depth) {
5493
+ const level = typeof depth === "number" ? depth : 0;
5494
+ if (typeof value === "string") return value.length > 240 ? value.slice(0, 237) + "..." : value;
5495
+ if (value === null || typeof value === "boolean" || typeof value === "number") return value;
5496
+ if (Array.isArray(value)) {
5497
+ if (level >= 2) return "[array:" + value.length + "]";
5498
+ return value.slice(0, 3).map((item) => compactJsonProbeSample(item, level + 1));
5499
+ }
5500
+ if (value && typeof value === "object") {
5501
+ const entries = Object.entries(value);
5502
+ if (level >= 2) return "[object:" + entries.length + " keys]";
5503
+ return Object.fromEntries(entries.slice(0, 8).map(([key, child]) => [key, compactJsonProbeSample(child, level + 1)]));
5504
+ }
5505
+ return String(value);
5506
+ }
5507
+ function attachJsonProbeObservedValue(result, value) {
5508
+ const type = jsonProbeValueType(value);
5509
+ if (type === "array" && Array.isArray(value)) {
5510
+ result.observed_length = value.length;
5511
+ result.observed_omitted_count = Math.max(0, value.length - 3);
5512
+ result.observed_sample = compactJsonProbeSample(value, 0);
5513
+ return;
5514
+ }
5515
+ if (type === "object" && value && typeof value === "object" && !Array.isArray(value)) {
5516
+ const keyCount = Object.keys(value).length;
5517
+ result.observed_key_count = keyCount;
5518
+ result.observed_omitted_count = Math.max(0, keyCount - 8);
5519
+ result.observed_sample = compactJsonProbeSample(value, 0);
5520
+ return;
5521
+ }
5522
+ result.observed = value;
5523
+ }
5321
5524
  function jsonProbeDeepEqual(left, right) {
5322
5525
  if (left === right) return true;
5323
5526
  if (typeof left !== typeof right) return false;
@@ -5414,7 +5617,7 @@ function evaluateJsonProbeAssertion(root, assertion) {
5414
5617
  exists: resolved.exists,
5415
5618
  observed_type: resolved.exists ? jsonProbeValueType(resolved.value) : "missing",
5416
5619
  };
5417
- if (resolved.exists) result.observed = resolved.value;
5620
+ if (resolved.exists) attachJsonProbeObservedValue(result, resolved.value);
5418
5621
  if (resolved.error) errors.push(resolved.error);
5419
5622
  if (Object.hasOwn(assertion, "exists")) {
5420
5623
  result.expected_exists = assertion.exists;
@@ -6227,6 +6430,7 @@ async function captureViewport(viewport) {
6227
6430
  const frames = {};
6228
6431
  const text_sequences = {};
6229
6432
  const text_matches = {};
6433
+ const text_match_samples = {};
6230
6434
  const http_statuses = {};
6231
6435
  const link_statuses = {};
6232
6436
  for (const check of profile.checks || []) {
@@ -6248,7 +6452,10 @@ async function captureViewport(viewport) {
6248
6452
  text_sequences[check.selector] = await selectorTextSequence(check.selector);
6249
6453
  }
6250
6454
  if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
6251
- text_matches[textKey(check)] = textMatches(dom.body_text || dom.body_text_sample || "", check);
6455
+ const key = textKey(check);
6456
+ const sample = dom.body_text || dom.body_text_sample || "";
6457
+ text_matches[key] = textMatches(sample, check);
6458
+ text_match_samples[key] = textMatchSamples(sample, check);
6252
6459
  }
6253
6460
  if ((check.type === "frame_text_visible" || check.type === "frame_url_equals" || check.type === "frame_url_matches" || check.type === "frame_no_horizontal_overflow") && check.selector) {
6254
6461
  selectors[check.selector] = selectors[check.selector] || await selectorStats(check.selector);
@@ -6325,6 +6532,7 @@ async function captureViewport(viewport) {
6325
6532
  frames,
6326
6533
  text_sequences,
6327
6534
  text_matches,
6535
+ text_match_samples,
6328
6536
  http_statuses,
6329
6537
  link_statuses,
6330
6538
  route_inventory: routeInventory,
@@ -54,6 +54,10 @@ interface RiddleProofProfileHttpStatusBodyJsonAssertionResult {
54
54
  ok: boolean;
55
55
  exists: boolean;
56
56
  observed?: JsonValue;
57
+ observed_sample?: JsonValue;
58
+ observed_length?: number;
59
+ observed_key_count?: number;
60
+ observed_omitted_count?: number;
57
61
  observed_type: RiddleProofProfileJsonValueType | "missing";
58
62
  expected_exists?: boolean;
59
63
  equals?: JsonValue;
@@ -303,6 +307,7 @@ interface RiddleProofProfileViewportEvidence {
303
307
  frames?: Record<string, Record<string, JsonValue>>;
304
308
  text_sequences?: Record<string, Record<string, JsonValue>>;
305
309
  text_matches?: Record<string, boolean>;
310
+ text_match_samples?: Record<string, string[]>;
306
311
  http_statuses?: Record<string, Record<string, JsonValue>>;
307
312
  link_statuses?: Record<string, Record<string, JsonValue>>;
308
313
  route_inventory?: Record<string, JsonValue>;
package/dist/profile.d.ts CHANGED
@@ -54,6 +54,10 @@ interface RiddleProofProfileHttpStatusBodyJsonAssertionResult {
54
54
  ok: boolean;
55
55
  exists: boolean;
56
56
  observed?: JsonValue;
57
+ observed_sample?: JsonValue;
58
+ observed_length?: number;
59
+ observed_key_count?: number;
60
+ observed_omitted_count?: number;
57
61
  observed_type: RiddleProofProfileJsonValueType | "missing";
58
62
  expected_exists?: boolean;
59
63
  equals?: JsonValue;
@@ -303,6 +307,7 @@ interface RiddleProofProfileViewportEvidence {
303
307
  frames?: Record<string, Record<string, JsonValue>>;
304
308
  text_sequences?: Record<string, Record<string, JsonValue>>;
305
309
  text_matches?: Record<string, boolean>;
310
+ text_match_samples?: Record<string, string[]>;
306
311
  http_statuses?: Record<string, Record<string, JsonValue>>;
307
312
  link_statuses?: Record<string, Record<string, JsonValue>>;
308
313
  route_inventory?: Record<string, JsonValue>;
package/dist/profile.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  resolveRiddleProofProfileTimeoutSec,
24
24
  slugifyRiddleProofProfileName,
25
25
  summarizeRiddleProofProfileResult
26
- } from "./chunk-JLINSUKO.js";
26
+ } from "./chunk-TDK6WFH3.js";
27
27
  export {
28
28
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
29
29
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.127",
3
+ "version": "0.7.129",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",