@riddledc/riddle-proof 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/dist/chunk-7NMAU4DP.js +808 -0
- package/dist/cli.cjs +950 -0
- package/dist/cli.js +192 -3
- package/dist/engine-harness.js +2 -2
- package/dist/index.cjs +823 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +38 -2
- package/dist/profile.cjs +848 -0
- package/dist/profile.d.cts +175 -0
- package/dist/profile.d.ts +175 -0
- package/dist/profile.js +38 -0
- package/examples/profiles/page-content-basic.json +26 -0
- package/package.json +8 -2
- package/dist/{chunk-2FBF2UDZ.js → chunk-D3M2FAYQ.js} +3 -3
package/dist/index.cjs
CHANGED
|
@@ -2788,6 +2788,11 @@ __export(index_exports, {
|
|
|
2788
2788
|
RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION: () => RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION,
|
|
2789
2789
|
RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION: () => RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
|
|
2790
2790
|
RIDDLE_PROOF_PLAYABILITY_VERSION: () => RIDDLE_PROOF_PLAYABILITY_VERSION,
|
|
2791
|
+
RIDDLE_PROOF_PROFILE_CHECK_TYPES: () => RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
2792
|
+
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: () => RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
2793
|
+
RIDDLE_PROOF_PROFILE_RESULT_VERSION: () => RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
2794
|
+
RIDDLE_PROOF_PROFILE_STATUSES: () => RIDDLE_PROOF_PROFILE_STATUSES,
|
|
2795
|
+
RIDDLE_PROOF_PROFILE_VERSION: () => RIDDLE_PROOF_PROFILE_VERSION,
|
|
2791
2796
|
RIDDLE_PROOF_RUN_CARD_VERSION: () => RIDDLE_PROOF_RUN_CARD_VERSION,
|
|
2792
2797
|
RIDDLE_PROOF_RUN_STATE_VERSION: () => RIDDLE_PROOF_RUN_STATE_VERSION,
|
|
2793
2798
|
RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION: () => RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION,
|
|
@@ -2803,16 +2808,19 @@ __export(index_exports, {
|
|
|
2803
2808
|
assessBasicGameplayProgressionChecks: () => assessBasicGameplayProgressionChecks,
|
|
2804
2809
|
assessBasicGameplayRoute: () => assessBasicGameplayRoute,
|
|
2805
2810
|
assessPlayabilityEvidence: () => assessPlayabilityEvidence,
|
|
2811
|
+
assessRiddleProofProfileEvidence: () => assessRiddleProofProfileEvidence,
|
|
2806
2812
|
attachBasicGameplayArtifactScreenshotHashes: () => attachBasicGameplayArtifactScreenshotHashes,
|
|
2807
2813
|
augmentBasicGameplayAssessmentWithProgressionChecks: () => augmentBasicGameplayAssessmentWithProgressionChecks,
|
|
2808
2814
|
authorPacketPayloadFromCheckpointResponse: () => authorPacketPayloadFromCheckpointResponse,
|
|
2809
2815
|
buildAuthorCheckpointPacket: () => buildAuthorCheckpointPacket,
|
|
2810
2816
|
buildCheckpointPacketForEngineResult: () => buildCheckpointPacketForEngineResult,
|
|
2811
2817
|
buildProofAssessmentCheckpointPacket: () => buildProofAssessmentCheckpointPacket,
|
|
2818
|
+
buildRiddleProofProfileScript: () => buildRiddleProofProfileScript,
|
|
2812
2819
|
buildStageCheckpointPacket: () => buildStageCheckpointPacket,
|
|
2813
2820
|
buildVisualProofSession: () => buildVisualProofSession,
|
|
2814
2821
|
checkpointResponseIdentity: () => checkpointResponseIdentity,
|
|
2815
2822
|
checkpointSummaryFromState: () => checkpointSummaryFromState,
|
|
2823
|
+
collectRiddleProfileArtifactRefs: () => collectRiddleProfileArtifactRefs,
|
|
2816
2824
|
compactBasicGameplayText: () => compactBasicGameplayText,
|
|
2817
2825
|
compactRecord: () => compactRecord,
|
|
2818
2826
|
compareVisualProofSessionFingerprint: () => compareVisualProofSessionFingerprint,
|
|
@@ -2826,6 +2834,9 @@ __export(index_exports, {
|
|
|
2826
2834
|
createLocalAgentAdapter: () => createCodexExecAgentAdapter,
|
|
2827
2835
|
createLocalAgentJsonRunner: () => createCodexExecJsonRunner,
|
|
2828
2836
|
createRiddleApiClient: () => createRiddleApiClient,
|
|
2837
|
+
createRiddleProofProfileConfigurationError: () => createRiddleProofProfileConfigurationError,
|
|
2838
|
+
createRiddleProofProfileEnvironmentBlockedResult: () => createRiddleProofProfileEnvironmentBlockedResult,
|
|
2839
|
+
createRiddleProofProfileInsufficientResult: () => createRiddleProofProfileInsufficientResult,
|
|
2829
2840
|
createRiddleProofRunCard: () => createRiddleProofRunCard,
|
|
2830
2841
|
createRunResult: () => createRunResult,
|
|
2831
2842
|
createRunState: () => createRunState,
|
|
@@ -2833,6 +2844,7 @@ __export(index_exports, {
|
|
|
2833
2844
|
deployRiddleStaticPreview: () => deployRiddleStaticPreview,
|
|
2834
2845
|
extractBasicGameplayEvidence: () => extractBasicGameplayEvidence,
|
|
2835
2846
|
extractPlayabilityEvidence: () => extractPlayabilityEvidence,
|
|
2847
|
+
extractRiddleProofProfileResult: () => extractRiddleProofProfileResult,
|
|
2836
2848
|
isDuplicateCheckpointResponse: () => isDuplicateCheckpointResponse,
|
|
2837
2849
|
isRiddleProofPlayabilityMode: () => isRiddleProofPlayabilityMode,
|
|
2838
2850
|
isSuccessfulStatus: () => isSuccessfulStatus,
|
|
@@ -2842,17 +2854,20 @@ __export(index_exports, {
|
|
|
2842
2854
|
normalizeCheckpointResponse: () => normalizeCheckpointResponse,
|
|
2843
2855
|
normalizeIntegrationContext: () => normalizeIntegrationContext,
|
|
2844
2856
|
normalizePrLifecycleState: () => normalizePrLifecycleState,
|
|
2857
|
+
normalizeRiddleProofProfile: () => normalizeRiddleProofProfile,
|
|
2845
2858
|
normalizeRunParams: () => normalizeRunParams,
|
|
2846
2859
|
normalizeTerminalMetadata: () => normalizeTerminalMetadata,
|
|
2847
2860
|
parseRiddleViewport: () => parseRiddleViewport,
|
|
2848
2861
|
parseVisualProofSession: () => parseVisualProofSession,
|
|
2849
2862
|
pollRiddleJob: () => pollRiddleJob,
|
|
2863
|
+
profileStatusExitCode: () => profileStatusExitCode,
|
|
2850
2864
|
proofContractFromAuthorCheckpointResponse: () => proofContractFromAuthorCheckpointResponse,
|
|
2851
2865
|
readRiddleProofRunStatus: () => readRiddleProofRunStatus,
|
|
2852
2866
|
recordValue: () => recordValue,
|
|
2853
2867
|
redactForProofDiagnostics: () => redactForProofDiagnostics,
|
|
2854
2868
|
resolveBasicGameplayProgressionCheckWithArtifactScreenshots: () => resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
|
|
2855
2869
|
resolveRiddleApiKey: () => resolveRiddleApiKey,
|
|
2870
|
+
resolveRiddleProofProfileTargetUrl: () => resolveRiddleProofProfileTargetUrl,
|
|
2856
2871
|
riddleRequestJson: () => riddleRequestJson,
|
|
2857
2872
|
runCodexExecAgentDoctor: () => runCodexExecAgentDoctor,
|
|
2858
2873
|
runLocalAgentDoctor: () => runCodexExecAgentDoctor,
|
|
@@ -2862,8 +2877,10 @@ __export(index_exports, {
|
|
|
2862
2877
|
runRiddleServerPreview: () => runRiddleServerPreview,
|
|
2863
2878
|
sanitizeBasicGameplayJsonString: () => sanitizeBasicGameplayJsonString,
|
|
2864
2879
|
setRunStatus: () => setRunStatus,
|
|
2880
|
+
slugifyRiddleProofProfileName: () => slugifyRiddleProofProfileName,
|
|
2865
2881
|
statePathsForRunState: () => statePathsForRunState,
|
|
2866
2882
|
summarizeCaptureArtifacts: () => summarizeCaptureArtifacts,
|
|
2883
|
+
summarizeRiddleProofProfileResult: () => summarizeRiddleProofProfileResult,
|
|
2867
2884
|
visualSessionFingerprint: () => visualSessionFingerprint,
|
|
2868
2885
|
visualSessionFingerprintBasis: () => visualSessionFingerprintBasis
|
|
2869
2886
|
});
|
|
@@ -8309,6 +8326,795 @@ function parseJson2(value) {
|
|
|
8309
8326
|
}
|
|
8310
8327
|
}
|
|
8311
8328
|
|
|
8329
|
+
// src/profile.ts
|
|
8330
|
+
var RIDDLE_PROOF_PROFILE_VERSION = "riddle-proof.profile.v1";
|
|
8331
|
+
var RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION = "riddle-proof.profile-evidence.v1";
|
|
8332
|
+
var RIDDLE_PROOF_PROFILE_RESULT_VERSION = "riddle-proof.profile-result.v1";
|
|
8333
|
+
var RIDDLE_PROOF_PROFILE_STATUSES = [
|
|
8334
|
+
"passed",
|
|
8335
|
+
"product_regression",
|
|
8336
|
+
"proof_insufficient",
|
|
8337
|
+
"environment_blocked",
|
|
8338
|
+
"configuration_error",
|
|
8339
|
+
"needs_human_review"
|
|
8340
|
+
];
|
|
8341
|
+
var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
8342
|
+
"route_loaded",
|
|
8343
|
+
"selector_visible",
|
|
8344
|
+
"selector_count_at_least",
|
|
8345
|
+
"text_visible",
|
|
8346
|
+
"text_absent",
|
|
8347
|
+
"no_horizontal_overflow",
|
|
8348
|
+
"no_mobile_horizontal_overflow",
|
|
8349
|
+
"no_fatal_console_errors"
|
|
8350
|
+
];
|
|
8351
|
+
var DEFAULT_VIEWPORTS = [
|
|
8352
|
+
{ name: "desktop", width: 1280, height: 800 }
|
|
8353
|
+
];
|
|
8354
|
+
function isRecord2(value) {
|
|
8355
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8356
|
+
}
|
|
8357
|
+
function stringValue5(value) {
|
|
8358
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
8359
|
+
}
|
|
8360
|
+
function numberValue3(value) {
|
|
8361
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
8362
|
+
}
|
|
8363
|
+
function jsonRecord(value) {
|
|
8364
|
+
if (!isRecord2(value)) return void 0;
|
|
8365
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
8366
|
+
}
|
|
8367
|
+
function toJsonValue(value) {
|
|
8368
|
+
if (value === null || value === void 0) return null;
|
|
8369
|
+
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
8370
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
8371
|
+
if (Array.isArray(value)) return value.map(toJsonValue);
|
|
8372
|
+
if (isRecord2(value)) return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
8373
|
+
return String(value);
|
|
8374
|
+
}
|
|
8375
|
+
function normalizeName(value, fallback) {
|
|
8376
|
+
const name = stringValue5(value) || fallback;
|
|
8377
|
+
return name.replace(/\s+/g, " ").trim();
|
|
8378
|
+
}
|
|
8379
|
+
function slugifyRiddleProofProfileName(value) {
|
|
8380
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "profile";
|
|
8381
|
+
}
|
|
8382
|
+
function normalizeViewport(input, index) {
|
|
8383
|
+
if (!isRecord2(input)) throw new Error(`target.viewports[${index}] must be an object.`);
|
|
8384
|
+
const width = numberValue3(input.width);
|
|
8385
|
+
const height = numberValue3(input.height);
|
|
8386
|
+
if (!width || !height || width < 100 || height < 100) {
|
|
8387
|
+
throw new Error(`target.viewports[${index}] requires numeric width and height >= 100.`);
|
|
8388
|
+
}
|
|
8389
|
+
return {
|
|
8390
|
+
name: normalizeName(input.name || input.label, `viewport-${index + 1}`),
|
|
8391
|
+
width: Math.round(width),
|
|
8392
|
+
height: Math.round(height)
|
|
8393
|
+
};
|
|
8394
|
+
}
|
|
8395
|
+
function normalizeViewports(value) {
|
|
8396
|
+
if (value === void 0) return [...DEFAULT_VIEWPORTS];
|
|
8397
|
+
if (!Array.isArray(value) || value.length === 0) throw new Error("target.viewports must be a non-empty array.");
|
|
8398
|
+
return value.map(normalizeViewport);
|
|
8399
|
+
}
|
|
8400
|
+
function isSupportedCheckType(value) {
|
|
8401
|
+
return RIDDLE_PROOF_PROFILE_CHECK_TYPES.includes(value);
|
|
8402
|
+
}
|
|
8403
|
+
function normalizeCheck(input, index) {
|
|
8404
|
+
if (!isRecord2(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
8405
|
+
const type = stringValue5(input.type);
|
|
8406
|
+
if (!type) throw new Error(`checks[${index}].type is required.`);
|
|
8407
|
+
if (!isSupportedCheckType(type)) {
|
|
8408
|
+
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
8409
|
+
}
|
|
8410
|
+
if ((type === "selector_visible" || type === "selector_count_at_least") && !stringValue5(input.selector)) {
|
|
8411
|
+
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
8412
|
+
}
|
|
8413
|
+
if ((type === "text_visible" || type === "text_absent") && !stringValue5(input.text) && !stringValue5(input.pattern)) {
|
|
8414
|
+
throw new Error(`checks[${index}] ${type} requires text or pattern.`);
|
|
8415
|
+
}
|
|
8416
|
+
if (type === "selector_count_at_least" && numberValue3(input.min_count) === void 0) {
|
|
8417
|
+
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
8418
|
+
}
|
|
8419
|
+
return {
|
|
8420
|
+
type,
|
|
8421
|
+
label: stringValue5(input.label),
|
|
8422
|
+
expected_path: stringValue5(input.expected_path),
|
|
8423
|
+
selector: stringValue5(input.selector),
|
|
8424
|
+
text: stringValue5(input.text),
|
|
8425
|
+
pattern: stringValue5(input.pattern),
|
|
8426
|
+
flags: stringValue5(input.flags),
|
|
8427
|
+
min_count: numberValue3(input.min_count),
|
|
8428
|
+
max_overflow_px: numberValue3(input.max_overflow_px)
|
|
8429
|
+
};
|
|
8430
|
+
}
|
|
8431
|
+
function normalizeFailurePolicy(input) {
|
|
8432
|
+
const defaults = {
|
|
8433
|
+
product_regression: "fail",
|
|
8434
|
+
proof_insufficient: "fail",
|
|
8435
|
+
environment_blocked: "neutral",
|
|
8436
|
+
configuration_error: "fail",
|
|
8437
|
+
needs_human_review: "fail"
|
|
8438
|
+
};
|
|
8439
|
+
if (!isRecord2(input)) return defaults;
|
|
8440
|
+
const next = { ...defaults };
|
|
8441
|
+
for (const [key, value] of Object.entries(input)) {
|
|
8442
|
+
if (!RIDDLE_PROOF_PROFILE_STATUSES.includes(key)) continue;
|
|
8443
|
+
if (value === "fail" || value === "neutral" || value === "review") {
|
|
8444
|
+
next[key] = value;
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
return next;
|
|
8448
|
+
}
|
|
8449
|
+
function normalizeRiddleProofProfile(input, options = {}) {
|
|
8450
|
+
if (!isRecord2(input)) throw new Error("profile must be a JSON object.");
|
|
8451
|
+
const version = stringValue5(input.version) || RIDDLE_PROOF_PROFILE_VERSION;
|
|
8452
|
+
if (version !== RIDDLE_PROOF_PROFILE_VERSION) {
|
|
8453
|
+
throw new Error(`Unsupported profile version ${version}. Expected ${RIDDLE_PROOF_PROFILE_VERSION}.`);
|
|
8454
|
+
}
|
|
8455
|
+
const targetInput = isRecord2(input.target) ? input.target : {};
|
|
8456
|
+
const checks = Array.isArray(input.checks) ? input.checks.map(normalizeCheck) : [];
|
|
8457
|
+
if (!checks.length) throw new Error("profile.checks must contain at least one check.");
|
|
8458
|
+
const targetUrl = stringValue5(options.url) || stringValue5(targetInput.url);
|
|
8459
|
+
const route = stringValue5(options.route) || stringValue5(targetInput.route);
|
|
8460
|
+
if (!targetUrl && !route) throw new Error("profile.target requires url or route, or pass --url.");
|
|
8461
|
+
return {
|
|
8462
|
+
version: RIDDLE_PROOF_PROFILE_VERSION,
|
|
8463
|
+
name: normalizeName(input.name, "riddle-proof-profile"),
|
|
8464
|
+
target: {
|
|
8465
|
+
url: targetUrl,
|
|
8466
|
+
route,
|
|
8467
|
+
viewports: options.viewports?.length ? options.viewports : normalizeViewports(targetInput.viewports),
|
|
8468
|
+
auth: stringValue5(targetInput.auth) || "none",
|
|
8469
|
+
wait_for_selector: stringValue5(targetInput.wait_for_selector) || stringValue5(targetInput.waitForSelector),
|
|
8470
|
+
wait_ms: numberValue3(targetInput.wait_ms) ?? numberValue3(targetInput.waitMs)
|
|
8471
|
+
},
|
|
8472
|
+
checks,
|
|
8473
|
+
artifacts: Array.isArray(input.artifacts) ? input.artifacts.map((item) => String(item)).filter(Boolean) : ["screenshot", "console", "dom_summary", "proof_json"],
|
|
8474
|
+
baseline_policy: stringValue5(input.baseline_policy) || stringValue5(input.baselinePolicy) || "invariant_only",
|
|
8475
|
+
failure_policy: normalizeFailurePolicy(input.failure_policy || input.failurePolicy),
|
|
8476
|
+
metadata: jsonRecord(input.metadata)
|
|
8477
|
+
};
|
|
8478
|
+
}
|
|
8479
|
+
function resolveRiddleProofProfileTargetUrl(profile) {
|
|
8480
|
+
const route = profile.target.route || "";
|
|
8481
|
+
const targetUrl = profile.target.url || "";
|
|
8482
|
+
if (/^https?:\/\//i.test(route)) return route;
|
|
8483
|
+
if (targetUrl && route) return new URL(route, targetUrl).href;
|
|
8484
|
+
if (targetUrl) return targetUrl;
|
|
8485
|
+
throw new Error("profile target URL could not be resolved.");
|
|
8486
|
+
}
|
|
8487
|
+
function routeForViewport(viewport) {
|
|
8488
|
+
return viewport?.route || {
|
|
8489
|
+
requested: "",
|
|
8490
|
+
observed: "",
|
|
8491
|
+
matched: false,
|
|
8492
|
+
error: "missing viewport evidence"
|
|
8493
|
+
};
|
|
8494
|
+
}
|
|
8495
|
+
function checkLabel(check) {
|
|
8496
|
+
return check.label || check.type;
|
|
8497
|
+
}
|
|
8498
|
+
function selectorKey(check) {
|
|
8499
|
+
return check.selector || "";
|
|
8500
|
+
}
|
|
8501
|
+
function textKey(check) {
|
|
8502
|
+
return check.pattern ? `pattern:${check.pattern}/${check.flags || ""}` : `text:${check.text || ""}`;
|
|
8503
|
+
}
|
|
8504
|
+
function matchText(sample, check) {
|
|
8505
|
+
if (check.pattern) {
|
|
8506
|
+
try {
|
|
8507
|
+
return new RegExp(check.pattern, check.flags || "").test(sample);
|
|
8508
|
+
} catch {
|
|
8509
|
+
return false;
|
|
8510
|
+
}
|
|
8511
|
+
}
|
|
8512
|
+
return sample.includes(check.text || "");
|
|
8513
|
+
}
|
|
8514
|
+
function successfulRoute(route) {
|
|
8515
|
+
return route.matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
|
|
8516
|
+
}
|
|
8517
|
+
function assessCheckFromEvidence(check, evidence) {
|
|
8518
|
+
const viewports = evidence.viewports || [];
|
|
8519
|
+
if (!viewports.length) {
|
|
8520
|
+
return {
|
|
8521
|
+
type: check.type,
|
|
8522
|
+
label: checkLabel(check),
|
|
8523
|
+
status: "failed",
|
|
8524
|
+
evidence: {},
|
|
8525
|
+
message: "No viewport evidence was captured."
|
|
8526
|
+
};
|
|
8527
|
+
}
|
|
8528
|
+
if (check.type === "route_loaded") {
|
|
8529
|
+
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
8530
|
+
const failed = viewports.filter((viewport) => !successfulRoute({
|
|
8531
|
+
...viewport.route,
|
|
8532
|
+
expected_path: expectedPath,
|
|
8533
|
+
matched: viewport.route.observed === expectedPath || viewport.route.matched
|
|
8534
|
+
}));
|
|
8535
|
+
return {
|
|
8536
|
+
type: check.type,
|
|
8537
|
+
label: checkLabel(check),
|
|
8538
|
+
status: failed.length ? "failed" : "passed",
|
|
8539
|
+
evidence: {
|
|
8540
|
+
expected_path: expectedPath,
|
|
8541
|
+
observed_paths: viewports.map((viewport) => viewport.route.observed),
|
|
8542
|
+
http_statuses: viewports.map((viewport) => viewport.route.http_status ?? null)
|
|
8543
|
+
},
|
|
8544
|
+
message: failed.length ? `Route did not load as ${expectedPath} in ${failed.length} viewport(s).` : void 0
|
|
8545
|
+
};
|
|
8546
|
+
}
|
|
8547
|
+
if (check.type === "selector_visible") {
|
|
8548
|
+
const key = selectorKey(check);
|
|
8549
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.visible_count || 0) < 1);
|
|
8550
|
+
return {
|
|
8551
|
+
type: check.type,
|
|
8552
|
+
label: checkLabel(check),
|
|
8553
|
+
status: failed.length ? "failed" : "passed",
|
|
8554
|
+
evidence: {
|
|
8555
|
+
selector: key,
|
|
8556
|
+
visible_counts: viewports.map((viewport) => viewport.selectors?.[key]?.visible_count || 0)
|
|
8557
|
+
},
|
|
8558
|
+
message: failed.length ? `Selector ${key} was not visible in ${failed.length} viewport(s).` : void 0
|
|
8559
|
+
};
|
|
8560
|
+
}
|
|
8561
|
+
if (check.type === "selector_count_at_least") {
|
|
8562
|
+
const key = selectorKey(check);
|
|
8563
|
+
const minCount = check.min_count ?? 1;
|
|
8564
|
+
const failed = viewports.filter((viewport) => (viewport.selectors?.[key]?.count || 0) < minCount);
|
|
8565
|
+
return {
|
|
8566
|
+
type: check.type,
|
|
8567
|
+
label: checkLabel(check),
|
|
8568
|
+
status: failed.length ? "failed" : "passed",
|
|
8569
|
+
evidence: {
|
|
8570
|
+
selector: key,
|
|
8571
|
+
min_count: minCount,
|
|
8572
|
+
counts: viewports.map((viewport) => viewport.selectors?.[key]?.count || 0)
|
|
8573
|
+
},
|
|
8574
|
+
message: failed.length ? `Selector ${key} count was below ${minCount} in ${failed.length} viewport(s).` : void 0
|
|
8575
|
+
};
|
|
8576
|
+
}
|
|
8577
|
+
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
8578
|
+
const key = textKey(check);
|
|
8579
|
+
const expectedVisible = check.type === "text_visible";
|
|
8580
|
+
const matches = viewports.map((viewport) => {
|
|
8581
|
+
const fromEvidence = viewport.text_matches?.[key];
|
|
8582
|
+
return typeof fromEvidence === "boolean" ? fromEvidence : matchText(viewport.body_text_sample || "", check);
|
|
8583
|
+
});
|
|
8584
|
+
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
8585
|
+
return {
|
|
8586
|
+
type: check.type,
|
|
8587
|
+
label: checkLabel(check),
|
|
8588
|
+
status: failed ? "failed" : "passed",
|
|
8589
|
+
evidence: {
|
|
8590
|
+
text: check.text || null,
|
|
8591
|
+
pattern: check.pattern || null,
|
|
8592
|
+
matches
|
|
8593
|
+
},
|
|
8594
|
+
message: failed ? `Text assertion failed in ${failed} viewport(s).` : void 0
|
|
8595
|
+
};
|
|
8596
|
+
}
|
|
8597
|
+
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
8598
|
+
const maxOverflow = check.max_overflow_px ?? 4;
|
|
8599
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
|
|
8600
|
+
if (!applicable.length) {
|
|
8601
|
+
return {
|
|
8602
|
+
type: check.type,
|
|
8603
|
+
label: checkLabel(check),
|
|
8604
|
+
status: "failed",
|
|
8605
|
+
evidence: { max_overflow_px: maxOverflow },
|
|
8606
|
+
message: "No applicable viewport evidence was captured for overflow check."
|
|
8607
|
+
};
|
|
8608
|
+
}
|
|
8609
|
+
const failed = applicable.filter((viewport) => (viewport.overflow_px ?? 0) > maxOverflow);
|
|
8610
|
+
return {
|
|
8611
|
+
type: check.type,
|
|
8612
|
+
label: checkLabel(check),
|
|
8613
|
+
status: failed.length ? "failed" : "passed",
|
|
8614
|
+
evidence: {
|
|
8615
|
+
max_overflow_px: maxOverflow,
|
|
8616
|
+
overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null),
|
|
8617
|
+
viewports: applicable.map((viewport) => viewport.name)
|
|
8618
|
+
},
|
|
8619
|
+
message: failed.length ? `Horizontal overflow exceeded ${maxOverflow}px in ${failed.length} viewport(s).` : void 0
|
|
8620
|
+
};
|
|
8621
|
+
}
|
|
8622
|
+
if (check.type === "no_fatal_console_errors") {
|
|
8623
|
+
const fatalCount = (evidence.console?.fatal_count || 0) + (evidence.page_errors?.length || 0);
|
|
8624
|
+
return {
|
|
8625
|
+
type: check.type,
|
|
8626
|
+
label: checkLabel(check),
|
|
8627
|
+
status: fatalCount ? "failed" : "passed",
|
|
8628
|
+
evidence: {
|
|
8629
|
+
console_fatal_count: evidence.console?.fatal_count || 0,
|
|
8630
|
+
page_error_count: evidence.page_errors?.length || 0
|
|
8631
|
+
},
|
|
8632
|
+
message: fatalCount ? `${fatalCount} fatal browser error(s) were captured.` : void 0
|
|
8633
|
+
};
|
|
8634
|
+
}
|
|
8635
|
+
return {
|
|
8636
|
+
type: check.type,
|
|
8637
|
+
label: checkLabel(check),
|
|
8638
|
+
status: "needs_human_review",
|
|
8639
|
+
evidence: {},
|
|
8640
|
+
message: "Unsupported check type."
|
|
8641
|
+
};
|
|
8642
|
+
}
|
|
8643
|
+
function profileStatusFromEvidence(evidence, checks) {
|
|
8644
|
+
if (!evidence) return "proof_insufficient";
|
|
8645
|
+
const viewports = evidence.viewports || [];
|
|
8646
|
+
if (!viewports.length || !checks.length) return "proof_insufficient";
|
|
8647
|
+
if (viewports.some((viewport) => viewport.navigation_error)) return "environment_blocked";
|
|
8648
|
+
if (checks.some((check) => check.status === "needs_human_review")) return "needs_human_review";
|
|
8649
|
+
if (checks.some((check) => check.status === "failed")) return "product_regression";
|
|
8650
|
+
return "passed";
|
|
8651
|
+
}
|
|
8652
|
+
function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
8653
|
+
const capturedAt = evidence?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
8654
|
+
const checks = evidence ? profile.checks.map((check) => assessCheckFromEvidence(check, evidence)) : [];
|
|
8655
|
+
const status = profileStatusFromEvidence(evidence, checks);
|
|
8656
|
+
const firstViewport = evidence?.viewports?.[0];
|
|
8657
|
+
const screenshots = (evidence?.viewports || []).map((viewport) => viewport.screenshot_label).filter((label) => Boolean(label));
|
|
8658
|
+
return {
|
|
8659
|
+
version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
8660
|
+
profile_name: profile.name,
|
|
8661
|
+
runner: options.runner || "riddle",
|
|
8662
|
+
status,
|
|
8663
|
+
baseline_policy: profile.baseline_policy,
|
|
8664
|
+
route: routeForViewport(firstViewport),
|
|
8665
|
+
artifacts: {
|
|
8666
|
+
screenshots,
|
|
8667
|
+
console: "console.json",
|
|
8668
|
+
proof_json: "proof.json",
|
|
8669
|
+
dom_summary: "dom-summary.json",
|
|
8670
|
+
riddle_artifacts: options.artifacts
|
|
8671
|
+
},
|
|
8672
|
+
checks,
|
|
8673
|
+
summary: summarizeRiddleProofProfileResult({ profile_name: profile.name, status, checks, viewports: evidence?.viewports || [] }),
|
|
8674
|
+
captured_at: capturedAt,
|
|
8675
|
+
evidence,
|
|
8676
|
+
riddle: options.riddle
|
|
8677
|
+
};
|
|
8678
|
+
}
|
|
8679
|
+
function summarizeRiddleProofProfileResult(input) {
|
|
8680
|
+
const passedChecks = input.checks.filter((check) => check.status === "passed").length;
|
|
8681
|
+
const failedChecks = input.checks.filter((check) => check.status === "failed").length;
|
|
8682
|
+
const viewportNames = input.viewports.map((viewport) => viewport.name).join(", ");
|
|
8683
|
+
if (input.status === "passed") {
|
|
8684
|
+
return `${input.profile_name} passed ${passedChecks} check(s) across ${input.viewports.length} viewport(s)${viewportNames ? ` (${viewportNames})` : ""}.`;
|
|
8685
|
+
}
|
|
8686
|
+
if (input.status === "product_regression") {
|
|
8687
|
+
return `${input.profile_name} failed ${failedChecks} product invariant(s) across ${input.viewports.length} viewport(s).`;
|
|
8688
|
+
}
|
|
8689
|
+
if (input.status === "environment_blocked") {
|
|
8690
|
+
return `${input.profile_name} could not collect reliable evidence because navigation or the browser environment was blocked.`;
|
|
8691
|
+
}
|
|
8692
|
+
if (input.status === "proof_insufficient") {
|
|
8693
|
+
return `${input.profile_name} did not produce enough evidence for a profile judgment.`;
|
|
8694
|
+
}
|
|
8695
|
+
if (input.status === "configuration_error") {
|
|
8696
|
+
return `${input.profile_name} has a profile configuration error.`;
|
|
8697
|
+
}
|
|
8698
|
+
return `${input.profile_name} collected artifacts but needs human review.`;
|
|
8699
|
+
}
|
|
8700
|
+
function profileStatusExitCode(profile, status) {
|
|
8701
|
+
if (status === "passed") return 0;
|
|
8702
|
+
return profile.failure_policy[status] === "neutral" ? 0 : 1;
|
|
8703
|
+
}
|
|
8704
|
+
function createRiddleProofProfileConfigurationError(name, error, runner = "riddle") {
|
|
8705
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8706
|
+
return {
|
|
8707
|
+
version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
8708
|
+
profile_name: name,
|
|
8709
|
+
runner,
|
|
8710
|
+
status: "configuration_error",
|
|
8711
|
+
baseline_policy: "invariant_only",
|
|
8712
|
+
route: { requested: "", observed: "", matched: false, error: message },
|
|
8713
|
+
artifacts: { screenshots: [], proof_json: "proof.json" },
|
|
8714
|
+
checks: [],
|
|
8715
|
+
summary: `${name} has a profile configuration error.`,
|
|
8716
|
+
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8717
|
+
error: message
|
|
8718
|
+
};
|
|
8719
|
+
}
|
|
8720
|
+
function createRiddleProofProfileEnvironmentBlockedResult(input) {
|
|
8721
|
+
const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "Riddle runner did not complete successfully.";
|
|
8722
|
+
return {
|
|
8723
|
+
version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
8724
|
+
profile_name: input.profile.name,
|
|
8725
|
+
runner: input.runner || "riddle",
|
|
8726
|
+
status: "environment_blocked",
|
|
8727
|
+
baseline_policy: input.profile.baseline_policy,
|
|
8728
|
+
route: { requested: resolveRiddleProofProfileTargetUrl(input.profile), observed: "", matched: false, error: message },
|
|
8729
|
+
artifacts: { screenshots: [], proof_json: "proof.json", riddle_artifacts: input.artifacts },
|
|
8730
|
+
checks: [],
|
|
8731
|
+
summary: `${input.profile.name} could not collect reliable evidence because the runner was blocked.`,
|
|
8732
|
+
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8733
|
+
riddle: input.riddle,
|
|
8734
|
+
error: message
|
|
8735
|
+
};
|
|
8736
|
+
}
|
|
8737
|
+
function createRiddleProofProfileInsufficientResult(input) {
|
|
8738
|
+
const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "No proof.json profile result artifact was found.";
|
|
8739
|
+
return {
|
|
8740
|
+
version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
8741
|
+
profile_name: input.profile.name,
|
|
8742
|
+
runner: input.runner || "riddle",
|
|
8743
|
+
status: "proof_insufficient",
|
|
8744
|
+
baseline_policy: input.profile.baseline_policy,
|
|
8745
|
+
route: { requested: resolveRiddleProofProfileTargetUrl(input.profile), observed: "", matched: false, error: message },
|
|
8746
|
+
artifacts: { screenshots: [], proof_json: "proof.json", riddle_artifacts: input.artifacts },
|
|
8747
|
+
checks: [],
|
|
8748
|
+
summary: `${input.profile.name} did not produce enough evidence for a profile judgment.`,
|
|
8749
|
+
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8750
|
+
riddle: input.riddle,
|
|
8751
|
+
error: message
|
|
8752
|
+
};
|
|
8753
|
+
}
|
|
8754
|
+
function runtimeScriptAssessmentSource() {
|
|
8755
|
+
return String.raw`
|
|
8756
|
+
function routeOk(route) {
|
|
8757
|
+
return Boolean(route && route.matched && !route.error && (route.http_status == null || route.http_status < 400));
|
|
8758
|
+
}
|
|
8759
|
+
function textMatches(sample, check) {
|
|
8760
|
+
if (check.pattern) {
|
|
8761
|
+
try { return new RegExp(check.pattern, check.flags || "").test(sample || ""); } catch { return false; }
|
|
8762
|
+
}
|
|
8763
|
+
return String(sample || "").includes(check.text || "");
|
|
8764
|
+
}
|
|
8765
|
+
function assessProfile(profile, evidence) {
|
|
8766
|
+
const checks = [];
|
|
8767
|
+
const viewports = evidence.viewports || [];
|
|
8768
|
+
for (const check of profile.checks || []) {
|
|
8769
|
+
if (check.type === "route_loaded") {
|
|
8770
|
+
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
8771
|
+
const failed = viewports.filter((viewport) => {
|
|
8772
|
+
const route = { ...(viewport.route || {}), expected_path: expectedPath };
|
|
8773
|
+
route.matched = route.observed === expectedPath || route.matched;
|
|
8774
|
+
return !routeOk(route);
|
|
8775
|
+
});
|
|
8776
|
+
checks.push({
|
|
8777
|
+
type: check.type,
|
|
8778
|
+
label: check.label || check.type,
|
|
8779
|
+
status: failed.length ? "failed" : "passed",
|
|
8780
|
+
evidence: {
|
|
8781
|
+
expected_path: expectedPath,
|
|
8782
|
+
observed_paths: viewports.map((viewport) => viewport.route && viewport.route.observed),
|
|
8783
|
+
http_statuses: viewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
|
|
8784
|
+
},
|
|
8785
|
+
message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
|
|
8786
|
+
});
|
|
8787
|
+
continue;
|
|
8788
|
+
}
|
|
8789
|
+
if (check.type === "selector_visible") {
|
|
8790
|
+
const selector = check.selector || "";
|
|
8791
|
+
const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
|
|
8792
|
+
checks.push({
|
|
8793
|
+
type: check.type,
|
|
8794
|
+
label: check.label || check.type,
|
|
8795
|
+
status: failed.length ? "failed" : "passed",
|
|
8796
|
+
evidence: { selector, visible_counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
|
|
8797
|
+
message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
|
|
8798
|
+
});
|
|
8799
|
+
continue;
|
|
8800
|
+
}
|
|
8801
|
+
if (check.type === "selector_count_at_least") {
|
|
8802
|
+
const selector = check.selector || "";
|
|
8803
|
+
const minCount = check.min_count == null ? 1 : check.min_count;
|
|
8804
|
+
const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
|
|
8805
|
+
checks.push({
|
|
8806
|
+
type: check.type,
|
|
8807
|
+
label: check.label || check.type,
|
|
8808
|
+
status: failed.length ? "failed" : "passed",
|
|
8809
|
+
evidence: { selector, min_count: minCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
|
|
8810
|
+
message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
|
|
8811
|
+
});
|
|
8812
|
+
continue;
|
|
8813
|
+
}
|
|
8814
|
+
if (check.type === "text_visible" || check.type === "text_absent") {
|
|
8815
|
+
const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
8816
|
+
const expectedVisible = check.type === "text_visible";
|
|
8817
|
+
const matches = viewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
|
|
8818
|
+
const failed = matches.filter((matched) => matched !== expectedVisible).length;
|
|
8819
|
+
checks.push({
|
|
8820
|
+
type: check.type,
|
|
8821
|
+
label: check.label || check.type,
|
|
8822
|
+
status: failed ? "failed" : "passed",
|
|
8823
|
+
evidence: { text: check.text, pattern: check.pattern, matches },
|
|
8824
|
+
message: failed ? "Text assertion failed in " + failed + " viewport(s)." : undefined,
|
|
8825
|
+
});
|
|
8826
|
+
continue;
|
|
8827
|
+
}
|
|
8828
|
+
if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
|
|
8829
|
+
const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
|
|
8830
|
+
const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
|
|
8831
|
+
if (!applicable.length) {
|
|
8832
|
+
checks.push({
|
|
8833
|
+
type: check.type,
|
|
8834
|
+
label: check.label || check.type,
|
|
8835
|
+
status: "failed",
|
|
8836
|
+
evidence: { max_overflow_px: maxOverflow },
|
|
8837
|
+
message: "No applicable viewport evidence was captured for overflow check.",
|
|
8838
|
+
});
|
|
8839
|
+
continue;
|
|
8840
|
+
}
|
|
8841
|
+
const failed = applicable.filter((viewport) => (viewport.overflow_px || 0) > maxOverflow);
|
|
8842
|
+
checks.push({
|
|
8843
|
+
type: check.type,
|
|
8844
|
+
label: check.label || check.type,
|
|
8845
|
+
status: failed.length ? "failed" : "passed",
|
|
8846
|
+
evidence: { max_overflow_px: maxOverflow, overflow_px: applicable.map((viewport) => viewport.overflow_px ?? null), viewports: applicable.map((viewport) => viewport.name) },
|
|
8847
|
+
message: failed.length ? "Horizontal overflow exceeded " + maxOverflow + "px in " + failed.length + " viewport(s)." : undefined,
|
|
8848
|
+
});
|
|
8849
|
+
continue;
|
|
8850
|
+
}
|
|
8851
|
+
if (check.type === "no_fatal_console_errors") {
|
|
8852
|
+
const fatalCount = ((evidence.console && evidence.console.fatal_count) || 0) + ((evidence.page_errors || []).length);
|
|
8853
|
+
checks.push({
|
|
8854
|
+
type: check.type,
|
|
8855
|
+
label: check.label || check.type,
|
|
8856
|
+
status: fatalCount ? "failed" : "passed",
|
|
8857
|
+
evidence: { console_fatal_count: (evidence.console && evidence.console.fatal_count) || 0, page_error_count: (evidence.page_errors || []).length },
|
|
8858
|
+
message: fatalCount ? String(fatalCount) + " fatal browser error(s) were captured." : undefined,
|
|
8859
|
+
});
|
|
8860
|
+
continue;
|
|
8861
|
+
}
|
|
8862
|
+
checks.push({ type: check.type, label: check.label || check.type, status: "needs_human_review", evidence: {}, message: "Unsupported check type." });
|
|
8863
|
+
}
|
|
8864
|
+
let status = "passed";
|
|
8865
|
+
if (!viewports.length || !checks.length) status = "proof_insufficient";
|
|
8866
|
+
else if (viewports.some((viewport) => viewport.navigation_error)) status = "environment_blocked";
|
|
8867
|
+
else if (checks.some((check) => check.status === "needs_human_review")) status = "needs_human_review";
|
|
8868
|
+
else if (checks.some((check) => check.status === "failed")) status = "product_regression";
|
|
8869
|
+
const screenshotLabels = viewports.map((viewport) => viewport.screenshot_label).filter(Boolean);
|
|
8870
|
+
const route = viewports[0] && viewports[0].route ? viewports[0].route : { requested: evidence.target_url, observed: "", matched: false, error: "missing viewport evidence" };
|
|
8871
|
+
const passedChecks = checks.filter((check) => check.status === "passed").length;
|
|
8872
|
+
const failedChecks = checks.filter((check) => check.status === "failed").length;
|
|
8873
|
+
const viewportNames = viewports.map((viewport) => viewport.name).join(", ");
|
|
8874
|
+
let summary = profile.name + " collected artifacts but needs human review.";
|
|
8875
|
+
if (status === "passed") summary = profile.name + " passed " + passedChecks + " check(s) across " + viewports.length + " viewport(s)" + (viewportNames ? " (" + viewportNames + ")." : ".");
|
|
8876
|
+
if (status === "product_regression") summary = profile.name + " failed " + failedChecks + " product invariant(s) across " + viewports.length + " viewport(s).";
|
|
8877
|
+
if (status === "environment_blocked") summary = profile.name + " could not collect reliable evidence because navigation or the browser environment was blocked.";
|
|
8878
|
+
if (status === "proof_insufficient") summary = profile.name + " did not produce enough evidence for a profile judgment.";
|
|
8879
|
+
return {
|
|
8880
|
+
version: "riddle-proof.profile-result.v1",
|
|
8881
|
+
profile_name: profile.name,
|
|
8882
|
+
runner: "riddle",
|
|
8883
|
+
status,
|
|
8884
|
+
baseline_policy: profile.baseline_policy || "invariant_only",
|
|
8885
|
+
route,
|
|
8886
|
+
artifacts: { screenshots: screenshotLabels, console: "console.json", proof_json: "proof.json", dom_summary: "dom-summary.json" },
|
|
8887
|
+
checks,
|
|
8888
|
+
summary,
|
|
8889
|
+
captured_at: evidence.captured_at,
|
|
8890
|
+
evidence,
|
|
8891
|
+
};
|
|
8892
|
+
}
|
|
8893
|
+
`;
|
|
8894
|
+
}
|
|
8895
|
+
function buildRiddleProofProfileScript(profile) {
|
|
8896
|
+
const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
|
|
8897
|
+
const slug2 = slugifyRiddleProofProfileName(profile.name);
|
|
8898
|
+
const serializableProfile = JSON.stringify(profile);
|
|
8899
|
+
const serializableTargetUrl = JSON.stringify(targetUrl);
|
|
8900
|
+
const serializableSlug = JSON.stringify(slug2);
|
|
8901
|
+
return String.raw`
|
|
8902
|
+
const profile = ${serializableProfile};
|
|
8903
|
+
const targetUrl = ${serializableTargetUrl};
|
|
8904
|
+
const profileSlug = ${serializableSlug};
|
|
8905
|
+
const capturedAt = new Date().toISOString();
|
|
8906
|
+
const consoleEvents = [];
|
|
8907
|
+
const pageErrors = [];
|
|
8908
|
+
page.on("console", (message) => {
|
|
8909
|
+
const type = message.type();
|
|
8910
|
+
if (type === "error" || type === "warning" || type === "assert") {
|
|
8911
|
+
consoleEvents.push({
|
|
8912
|
+
type,
|
|
8913
|
+
text: message.text().slice(0, 1000),
|
|
8914
|
+
location: message.location ? message.location() : undefined,
|
|
8915
|
+
});
|
|
8916
|
+
}
|
|
8917
|
+
});
|
|
8918
|
+
page.on("pageerror", (error) => {
|
|
8919
|
+
pageErrors.push({ message: String(error && error.message ? error.message : error).slice(0, 1000) });
|
|
8920
|
+
});
|
|
8921
|
+
function textKey(check) {
|
|
8922
|
+
return check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
|
|
8923
|
+
}
|
|
8924
|
+
function textMatches(sample, check) {
|
|
8925
|
+
if (check.pattern) {
|
|
8926
|
+
try { return new RegExp(check.pattern, check.flags || "").test(sample || ""); } catch { return false; }
|
|
8927
|
+
}
|
|
8928
|
+
return String(sample || "").includes(check.text || "");
|
|
8929
|
+
}
|
|
8930
|
+
function expectedPathFor(check) {
|
|
8931
|
+
return check.expected_path || new URL(targetUrl).pathname || "/";
|
|
8932
|
+
}
|
|
8933
|
+
async function selectorStats(selector) {
|
|
8934
|
+
return page.locator(selector).evaluateAll((elements) => {
|
|
8935
|
+
const isVisible = (element) => {
|
|
8936
|
+
const style = window.getComputedStyle(element);
|
|
8937
|
+
const rect = element.getBoundingClientRect();
|
|
8938
|
+
return style && style.visibility !== "hidden" && style.display !== "none" && rect.width > 0 && rect.height > 0;
|
|
8939
|
+
};
|
|
8940
|
+
return { count: elements.length, visible_count: elements.filter(isVisible).length };
|
|
8941
|
+
}).catch((error) => ({ count: 0, visible_count: 0, error: String(error && error.message ? error.message : error).slice(0, 500) }));
|
|
8942
|
+
}
|
|
8943
|
+
async function captureViewport(viewport) {
|
|
8944
|
+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
8945
|
+
let httpStatus = null;
|
|
8946
|
+
let navigationError;
|
|
8947
|
+
let waitError;
|
|
8948
|
+
try {
|
|
8949
|
+
const response = await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 45000 });
|
|
8950
|
+
httpStatus = response ? response.status() : null;
|
|
8951
|
+
} catch (error) {
|
|
8952
|
+
navigationError = String(error && error.message ? error.message : error).slice(0, 1000);
|
|
8953
|
+
}
|
|
8954
|
+
if (!navigationError && profile.target.wait_for_selector) {
|
|
8955
|
+
try {
|
|
8956
|
+
await page.waitForSelector(profile.target.wait_for_selector, { state: "visible", timeout: 15000 });
|
|
8957
|
+
} catch (error) {
|
|
8958
|
+
waitError = String(error && error.message ? error.message : error).slice(0, 1000);
|
|
8959
|
+
}
|
|
8960
|
+
}
|
|
8961
|
+
if (!navigationError && profile.target.wait_ms) {
|
|
8962
|
+
await page.waitForTimeout(profile.target.wait_ms);
|
|
8963
|
+
}
|
|
8964
|
+
const dom = await page.evaluate(() => {
|
|
8965
|
+
const body = document.body;
|
|
8966
|
+
const documentElement = document.documentElement;
|
|
8967
|
+
const text = (body ? body.innerText : "").replace(/\s+/g, " ").trim();
|
|
8968
|
+
return {
|
|
8969
|
+
url: location.href,
|
|
8970
|
+
pathname: location.pathname,
|
|
8971
|
+
title: document.title,
|
|
8972
|
+
body_text_length: text.length,
|
|
8973
|
+
body_text_sample: text.slice(0, 8000),
|
|
8974
|
+
scroll_width: documentElement ? documentElement.scrollWidth : 0,
|
|
8975
|
+
client_width: documentElement ? documentElement.clientWidth : window.innerWidth,
|
|
8976
|
+
};
|
|
8977
|
+
}).catch((error) => ({
|
|
8978
|
+
url: page.url(),
|
|
8979
|
+
pathname: "",
|
|
8980
|
+
title: "",
|
|
8981
|
+
body_text_length: 0,
|
|
8982
|
+
body_text_sample: "",
|
|
8983
|
+
scroll_width: 0,
|
|
8984
|
+
client_width: viewport.width,
|
|
8985
|
+
evaluation_error: String(error && error.message ? error.message : error).slice(0, 1000),
|
|
8986
|
+
}));
|
|
8987
|
+
const selectors = {};
|
|
8988
|
+
const text_matches = {};
|
|
8989
|
+
for (const check of profile.checks || []) {
|
|
8990
|
+
if ((check.type === "selector_visible" || check.type === "selector_count_at_least") && check.selector) {
|
|
8991
|
+
selectors[check.selector] = await selectorStats(check.selector);
|
|
8992
|
+
}
|
|
8993
|
+
if ((check.type === "text_visible" || check.type === "text_absent") && (check.text || check.pattern)) {
|
|
8994
|
+
text_matches[textKey(check)] = textMatches(dom.body_text_sample || "", check);
|
|
8995
|
+
}
|
|
8996
|
+
}
|
|
8997
|
+
const screenshotLabel = profileSlug + "-" + viewport.name;
|
|
8998
|
+
try {
|
|
8999
|
+
if (typeof saveScreenshot === "function") await saveScreenshot(screenshotLabel);
|
|
9000
|
+
} catch (error) {
|
|
9001
|
+
pageErrors.push({ message: "saveScreenshot failed: " + String(error && error.message ? error.message : error).slice(0, 500) });
|
|
9002
|
+
}
|
|
9003
|
+
const expectedPath = profile.checks.find((check) => check.type === "route_loaded" && check.expected_path)?.expected_path || new URL(targetUrl).pathname || "/";
|
|
9004
|
+
return {
|
|
9005
|
+
name: viewport.name,
|
|
9006
|
+
width: viewport.width,
|
|
9007
|
+
height: viewport.height,
|
|
9008
|
+
url: dom.url,
|
|
9009
|
+
route: {
|
|
9010
|
+
requested: targetUrl,
|
|
9011
|
+
observed: dom.pathname,
|
|
9012
|
+
expected_path: expectedPath,
|
|
9013
|
+
matched: dom.pathname === expectedPath,
|
|
9014
|
+
http_status: httpStatus,
|
|
9015
|
+
error: navigationError || waitError || undefined,
|
|
9016
|
+
},
|
|
9017
|
+
title: dom.title,
|
|
9018
|
+
body_text_length: dom.body_text_length,
|
|
9019
|
+
body_text_sample: dom.body_text_sample,
|
|
9020
|
+
scroll_width: dom.scroll_width,
|
|
9021
|
+
client_width: dom.client_width,
|
|
9022
|
+
overflow_px: Math.max(0, (dom.scroll_width || 0) - (dom.client_width || viewport.width)),
|
|
9023
|
+
selectors,
|
|
9024
|
+
text_matches,
|
|
9025
|
+
screenshot_label: screenshotLabel,
|
|
9026
|
+
navigation_error: navigationError,
|
|
9027
|
+
wait_error: waitError,
|
|
9028
|
+
};
|
|
9029
|
+
}
|
|
9030
|
+
${runtimeScriptAssessmentSource()}
|
|
9031
|
+
const viewports = [];
|
|
9032
|
+
for (const viewport of profile.target.viewports || []) {
|
|
9033
|
+
viewports.push(await captureViewport(viewport));
|
|
9034
|
+
}
|
|
9035
|
+
const evidence = {
|
|
9036
|
+
version: "riddle-proof.profile-evidence.v1",
|
|
9037
|
+
profile_name: profile.name,
|
|
9038
|
+
target_url: targetUrl,
|
|
9039
|
+
baseline_policy: profile.baseline_policy || "invariant_only",
|
|
9040
|
+
captured_at: capturedAt,
|
|
9041
|
+
viewports,
|
|
9042
|
+
console: {
|
|
9043
|
+
events: consoleEvents,
|
|
9044
|
+
fatal_count: consoleEvents.filter((event) => event.type === "error" || event.type === "assert").length,
|
|
9045
|
+
},
|
|
9046
|
+
page_errors: pageErrors,
|
|
9047
|
+
dom_summary: {
|
|
9048
|
+
viewport_count: viewports.length,
|
|
9049
|
+
routes: viewports.map((viewport) => viewport.route),
|
|
9050
|
+
titles: viewports.map((viewport) => viewport.title),
|
|
9051
|
+
overflow_px: viewports.map((viewport) => viewport.overflow_px),
|
|
9052
|
+
},
|
|
9053
|
+
};
|
|
9054
|
+
const result = assessProfile(profile, evidence);
|
|
9055
|
+
if (typeof saveJson === "function") {
|
|
9056
|
+
await saveJson("proof.json", result);
|
|
9057
|
+
await saveJson("console.json", { events: consoleEvents, page_errors: pageErrors });
|
|
9058
|
+
await saveJson("dom-summary.json", evidence.dom_summary);
|
|
9059
|
+
}
|
|
9060
|
+
return result;
|
|
9061
|
+
`.trim();
|
|
9062
|
+
}
|
|
9063
|
+
function collectRiddleProfileArtifactRefs(input) {
|
|
9064
|
+
const refs = [];
|
|
9065
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9066
|
+
function add(item, source) {
|
|
9067
|
+
if (!isRecord2(item)) return;
|
|
9068
|
+
const name = stringValue5(item.name) || stringValue5(item.filename) || "";
|
|
9069
|
+
const url = stringValue5(item.url);
|
|
9070
|
+
const path6 = stringValue5(item.path);
|
|
9071
|
+
if (!name && !url && !path6) return;
|
|
9072
|
+
const key = `${name}:${url || path6 || ""}`;
|
|
9073
|
+
if (seen.has(key)) return;
|
|
9074
|
+
seen.add(key);
|
|
9075
|
+
refs.push({
|
|
9076
|
+
name,
|
|
9077
|
+
url,
|
|
9078
|
+
path: path6,
|
|
9079
|
+
kind: stringValue5(item.kind) || stringValue5(item.type),
|
|
9080
|
+
content_type: stringValue5(item.content_type) || stringValue5(item.contentType),
|
|
9081
|
+
source
|
|
9082
|
+
});
|
|
9083
|
+
}
|
|
9084
|
+
function visit(value, source) {
|
|
9085
|
+
if (Array.isArray(value)) {
|
|
9086
|
+
for (const item of value) add(item, source);
|
|
9087
|
+
return;
|
|
9088
|
+
}
|
|
9089
|
+
if (!isRecord2(value)) return;
|
|
9090
|
+
for (const key of ["artifacts", "outputs", "screenshots", "files"]) {
|
|
9091
|
+
if (Array.isArray(value[key])) visit(value[key], key);
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
visit(input, "artifacts");
|
|
9095
|
+
return refs;
|
|
9096
|
+
}
|
|
9097
|
+
function extractRiddleProofProfileResult(input) {
|
|
9098
|
+
if (!isRecord2(input)) return void 0;
|
|
9099
|
+
if (input.version === RIDDLE_PROOF_PROFILE_RESULT_VERSION) return input;
|
|
9100
|
+
const candidates = [
|
|
9101
|
+
input.result,
|
|
9102
|
+
input.return_value,
|
|
9103
|
+
input.value,
|
|
9104
|
+
input.profile_result,
|
|
9105
|
+
isRecord2(input["proof.json"]) ? input["proof.json"] : void 0,
|
|
9106
|
+
isRecord2(input._proof_json) ? input._proof_json : void 0
|
|
9107
|
+
];
|
|
9108
|
+
for (const candidate of candidates) {
|
|
9109
|
+
const result = extractRiddleProofProfileResult(candidate);
|
|
9110
|
+
if (result) return result;
|
|
9111
|
+
}
|
|
9112
|
+
if (isRecord2(input._artifact_json)) {
|
|
9113
|
+
return extractRiddleProofProfileResult(input._artifact_json["proof.json"]);
|
|
9114
|
+
}
|
|
9115
|
+
return void 0;
|
|
9116
|
+
}
|
|
9117
|
+
|
|
8312
9118
|
// src/riddle-client.ts
|
|
8313
9119
|
var import_node_child_process4 = require("child_process");
|
|
8314
9120
|
var import_node_fs5 = require("fs");
|
|
@@ -8558,6 +9364,11 @@ function createRiddleApiClient(config = {}) {
|
|
|
8558
9364
|
RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION,
|
|
8559
9365
|
RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION,
|
|
8560
9366
|
RIDDLE_PROOF_PLAYABILITY_VERSION,
|
|
9367
|
+
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
9368
|
+
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
9369
|
+
RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
9370
|
+
RIDDLE_PROOF_PROFILE_STATUSES,
|
|
9371
|
+
RIDDLE_PROOF_PROFILE_VERSION,
|
|
8561
9372
|
RIDDLE_PROOF_RUN_CARD_VERSION,
|
|
8562
9373
|
RIDDLE_PROOF_RUN_STATE_VERSION,
|
|
8563
9374
|
RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION,
|
|
@@ -8573,16 +9384,19 @@ function createRiddleApiClient(config = {}) {
|
|
|
8573
9384
|
assessBasicGameplayProgressionChecks,
|
|
8574
9385
|
assessBasicGameplayRoute,
|
|
8575
9386
|
assessPlayabilityEvidence,
|
|
9387
|
+
assessRiddleProofProfileEvidence,
|
|
8576
9388
|
attachBasicGameplayArtifactScreenshotHashes,
|
|
8577
9389
|
augmentBasicGameplayAssessmentWithProgressionChecks,
|
|
8578
9390
|
authorPacketPayloadFromCheckpointResponse,
|
|
8579
9391
|
buildAuthorCheckpointPacket,
|
|
8580
9392
|
buildCheckpointPacketForEngineResult,
|
|
8581
9393
|
buildProofAssessmentCheckpointPacket,
|
|
9394
|
+
buildRiddleProofProfileScript,
|
|
8582
9395
|
buildStageCheckpointPacket,
|
|
8583
9396
|
buildVisualProofSession,
|
|
8584
9397
|
checkpointResponseIdentity,
|
|
8585
9398
|
checkpointSummaryFromState,
|
|
9399
|
+
collectRiddleProfileArtifactRefs,
|
|
8586
9400
|
compactBasicGameplayText,
|
|
8587
9401
|
compactRecord,
|
|
8588
9402
|
compareVisualProofSessionFingerprint,
|
|
@@ -8596,6 +9410,9 @@ function createRiddleApiClient(config = {}) {
|
|
|
8596
9410
|
createLocalAgentAdapter,
|
|
8597
9411
|
createLocalAgentJsonRunner,
|
|
8598
9412
|
createRiddleApiClient,
|
|
9413
|
+
createRiddleProofProfileConfigurationError,
|
|
9414
|
+
createRiddleProofProfileEnvironmentBlockedResult,
|
|
9415
|
+
createRiddleProofProfileInsufficientResult,
|
|
8599
9416
|
createRiddleProofRunCard,
|
|
8600
9417
|
createRunResult,
|
|
8601
9418
|
createRunState,
|
|
@@ -8603,6 +9420,7 @@ function createRiddleApiClient(config = {}) {
|
|
|
8603
9420
|
deployRiddleStaticPreview,
|
|
8604
9421
|
extractBasicGameplayEvidence,
|
|
8605
9422
|
extractPlayabilityEvidence,
|
|
9423
|
+
extractRiddleProofProfileResult,
|
|
8606
9424
|
isDuplicateCheckpointResponse,
|
|
8607
9425
|
isRiddleProofPlayabilityMode,
|
|
8608
9426
|
isSuccessfulStatus,
|
|
@@ -8612,17 +9430,20 @@ function createRiddleApiClient(config = {}) {
|
|
|
8612
9430
|
normalizeCheckpointResponse,
|
|
8613
9431
|
normalizeIntegrationContext,
|
|
8614
9432
|
normalizePrLifecycleState,
|
|
9433
|
+
normalizeRiddleProofProfile,
|
|
8615
9434
|
normalizeRunParams,
|
|
8616
9435
|
normalizeTerminalMetadata,
|
|
8617
9436
|
parseRiddleViewport,
|
|
8618
9437
|
parseVisualProofSession,
|
|
8619
9438
|
pollRiddleJob,
|
|
9439
|
+
profileStatusExitCode,
|
|
8620
9440
|
proofContractFromAuthorCheckpointResponse,
|
|
8621
9441
|
readRiddleProofRunStatus,
|
|
8622
9442
|
recordValue,
|
|
8623
9443
|
redactForProofDiagnostics,
|
|
8624
9444
|
resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
|
|
8625
9445
|
resolveRiddleApiKey,
|
|
9446
|
+
resolveRiddleProofProfileTargetUrl,
|
|
8626
9447
|
riddleRequestJson,
|
|
8627
9448
|
runCodexExecAgentDoctor,
|
|
8628
9449
|
runLocalAgentDoctor,
|
|
@@ -8632,8 +9453,10 @@ function createRiddleApiClient(config = {}) {
|
|
|
8632
9453
|
runRiddleServerPreview,
|
|
8633
9454
|
sanitizeBasicGameplayJsonString,
|
|
8634
9455
|
setRunStatus,
|
|
9456
|
+
slugifyRiddleProofProfileName,
|
|
8635
9457
|
statePathsForRunState,
|
|
8636
9458
|
summarizeCaptureArtifacts,
|
|
9459
|
+
summarizeRiddleProofProfileResult,
|
|
8637
9460
|
visualSessionFingerprint,
|
|
8638
9461
|
visualSessionFingerprintBasis
|
|
8639
9462
|
});
|