@riddledc/riddle-proof 0.7.113 → 0.7.115
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 +13 -0
- package/dist/{chunk-OI57LO6Y.js → chunk-7GHBRZHQ.js} +58 -14
- package/dist/{chunk-3CND75DM.js → chunk-QOOPPRYK.js} +55 -1
- package/dist/cli.cjs +113 -15
- package/dist/cli.js +2 -2
- package/dist/index.cjs +113 -15
- package/dist/index.js +2 -2
- package/dist/profile.cjs +55 -1
- package/dist/profile.d.cts +1 -1
- package/dist/profile.d.ts +1 -1
- package/dist/profile.js +1 -1
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/dist/riddle-client.cjs +58 -14
- package/dist/riddle-client.d.cts +2 -0
- package/dist/riddle-client.d.ts +2 -0
- package/dist/riddle-client.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -406,6 +406,19 @@ These checks are useful for audit/no-diff profiles where the product should
|
|
|
406
406
|
show a fallback state and avoid rendering stale loaders, duplicate rows, or
|
|
407
407
|
missing-resource iframes.
|
|
408
408
|
|
|
409
|
+
Use `dialog_count_equals`, `dialog_accept_count_equals`, and
|
|
410
|
+
`dialog_dismiss_count_equals` when destructive-action profiles need to prove
|
|
411
|
+
browser confirm/prompt handling directly. The counts come from the captured
|
|
412
|
+
dialog summary and use `expected_count`:
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
[
|
|
416
|
+
{ "type": "dialog_count_equals", "expected_count": 2 },
|
|
417
|
+
{ "type": "dialog_accept_count_equals", "expected_count": 1 },
|
|
418
|
+
{ "type": "dialog_dismiss_count_equals", "expected_count": 1 }
|
|
419
|
+
]
|
|
420
|
+
```
|
|
421
|
+
|
|
409
422
|
Use `url_search_param_equals` and `url_search_param_absent` when the final URL
|
|
410
423
|
is part of the contract, such as deep-link recovery that must drop a stale
|
|
411
424
|
local identifier while preserving shareable query state:
|
|
@@ -14,6 +14,9 @@ var RiddleApiError = class extends Error {
|
|
|
14
14
|
this.path = pathname;
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
+
var PREVIEW_PUBLISH_RECOVERY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
18
|
+
var PREVIEW_PUBLISH_RECOVERY_ATTEMPTS = 30;
|
|
19
|
+
var PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS = 2e3;
|
|
17
20
|
function normalizeBaseUrl(value) {
|
|
18
21
|
return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
|
|
19
22
|
}
|
|
@@ -48,6 +51,45 @@ async function riddleRequestJson(config, pathname, init = {}) {
|
|
|
48
51
|
if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
|
|
49
52
|
return json ?? text;
|
|
50
53
|
}
|
|
54
|
+
function previewDeployResultFromRecord(input) {
|
|
55
|
+
const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
id: String(record.id || record.preview_id || id),
|
|
59
|
+
label,
|
|
60
|
+
framework,
|
|
61
|
+
preview_url: String(record.preview_url || ""),
|
|
62
|
+
file_count: typeof record.file_count === "number" ? record.file_count : void 0,
|
|
63
|
+
total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
|
|
64
|
+
expires_at: expiresAt,
|
|
65
|
+
publish_recovered: publishRecovered || void 0,
|
|
66
|
+
publish_error: publishError,
|
|
67
|
+
raw: record
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function canRecoverPreviewPublish(error) {
|
|
71
|
+
return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
|
|
72
|
+
}
|
|
73
|
+
async function waitForPublishedPreview(config, input) {
|
|
74
|
+
for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
|
|
75
|
+
const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
|
|
76
|
+
if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
|
|
77
|
+
return previewDeployResultFromRecord({
|
|
78
|
+
record: status,
|
|
79
|
+
id: input.id,
|
|
80
|
+
label: input.label,
|
|
81
|
+
framework: input.framework,
|
|
82
|
+
expiresAt: input.expiresAt,
|
|
83
|
+
publishRecovered: true,
|
|
84
|
+
publishError: input.publishError.message
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (attempt < PREVIEW_PUBLISH_RECOVERY_ATTEMPTS) {
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw input.publishError;
|
|
92
|
+
}
|
|
51
93
|
async function deployRiddlePreview(config, directory, label, framework = "static") {
|
|
52
94
|
if (!directory?.trim()) throw new Error("directory is required");
|
|
53
95
|
if (!label?.trim()) throw new Error("label is required");
|
|
@@ -74,20 +116,22 @@ async function deployRiddlePreview(config, directory, label, framework = "static
|
|
|
74
116
|
} finally {
|
|
75
117
|
rmSync(scratch, { recursive: true, force: true });
|
|
76
118
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
|
|
120
|
+
try {
|
|
121
|
+
const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
|
|
122
|
+
method: "POST"
|
|
123
|
+
});
|
|
124
|
+
return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt });
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (!canRecoverPreviewPublish(error)) throw error;
|
|
127
|
+
return waitForPublishedPreview(config, {
|
|
128
|
+
id,
|
|
129
|
+
label,
|
|
130
|
+
framework,
|
|
131
|
+
expiresAt,
|
|
132
|
+
publishError: error
|
|
133
|
+
});
|
|
134
|
+
}
|
|
91
135
|
}
|
|
92
136
|
async function deployRiddleStaticPreview(config, directory, label) {
|
|
93
137
|
return deployRiddlePreview(config, directory, label, "static");
|
|
@@ -20,6 +20,9 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
20
20
|
"selector_count_equals",
|
|
21
21
|
"selector_count_equal",
|
|
22
22
|
"selector_count_eq",
|
|
23
|
+
"dialog_count_equals",
|
|
24
|
+
"dialog_accept_count_equals",
|
|
25
|
+
"dialog_dismiss_count_equals",
|
|
23
26
|
"selector_text_visible",
|
|
24
27
|
"selector_text_absent",
|
|
25
28
|
"selector_text_order",
|
|
@@ -772,6 +775,14 @@ function validateRegexPatterns(patterns, label) {
|
|
|
772
775
|
}
|
|
773
776
|
}
|
|
774
777
|
}
|
|
778
|
+
function isDialogCountCheckType(type) {
|
|
779
|
+
return type === "dialog_count_equals" || type === "dialog_accept_count_equals" || type === "dialog_dismiss_count_equals";
|
|
780
|
+
}
|
|
781
|
+
function dialogCountFieldForCheckType(type) {
|
|
782
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
783
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
784
|
+
return "dialog_count";
|
|
785
|
+
}
|
|
775
786
|
function normalizeCheck(input, index) {
|
|
776
787
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
777
788
|
const type = stringValue(input.type);
|
|
@@ -779,6 +790,7 @@ function normalizeCheck(input, index) {
|
|
|
779
790
|
if (!isSupportedCheckType(type)) {
|
|
780
791
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
781
792
|
}
|
|
793
|
+
const isDialogCountCheck = isDialogCountCheckType(type);
|
|
782
794
|
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue(input.selector)) {
|
|
783
795
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
784
796
|
}
|
|
@@ -810,9 +822,12 @@ function normalizeCheck(input, index) {
|
|
|
810
822
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
811
823
|
}
|
|
812
824
|
const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
|
|
813
|
-
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
825
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || isDialogCountCheck) && expectedCount === void 0) {
|
|
814
826
|
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
815
827
|
}
|
|
828
|
+
if (isDialogCountCheck && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
|
|
829
|
+
throw new Error(`checks[${index}] ${type} expected_count must be a non-negative integer.`);
|
|
830
|
+
}
|
|
816
831
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
817
832
|
if (type === "selector_text_order") {
|
|
818
833
|
if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -1482,6 +1497,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1482
1497
|
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
1483
1498
|
};
|
|
1484
1499
|
}
|
|
1500
|
+
if (isDialogCountCheckType(check.type)) {
|
|
1501
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
1502
|
+
const expectedCount = check.expected_count ?? 0;
|
|
1503
|
+
const actualCount = numberValue(evidence.dom_summary?.[field]) ?? 0;
|
|
1504
|
+
return {
|
|
1505
|
+
type: check.type,
|
|
1506
|
+
label: checkLabel(check),
|
|
1507
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
1508
|
+
evidence: {
|
|
1509
|
+
field,
|
|
1510
|
+
expected_count: expectedCount,
|
|
1511
|
+
count: actualCount
|
|
1512
|
+
},
|
|
1513
|
+
message: actualCount === expectedCount ? void 0 : `${field} did not equal ${expectedCount}; observed ${actualCount}.`
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1485
1516
|
if (check.type === "route_loaded") {
|
|
1486
1517
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
1487
1518
|
const failed = viewports.filter((viewport) => !successfulRoute({
|
|
@@ -2733,6 +2764,16 @@ function summarizeRouteInventory(viewport, inventory) {
|
|
|
2733
2764
|
function numberValue(value) {
|
|
2734
2765
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
2735
2766
|
}
|
|
2767
|
+
function isDialogCountCheckType(type) {
|
|
2768
|
+
return type === "dialog_count_equals"
|
|
2769
|
+
|| type === "dialog_accept_count_equals"
|
|
2770
|
+
|| type === "dialog_dismiss_count_equals";
|
|
2771
|
+
}
|
|
2772
|
+
function dialogCountFieldForCheckType(type) {
|
|
2773
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
2774
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
2775
|
+
return "dialog_count";
|
|
2776
|
+
}
|
|
2736
2777
|
function maxPositiveNumber() {
|
|
2737
2778
|
let max = 0;
|
|
2738
2779
|
for (const value of arguments) {
|
|
@@ -3104,6 +3145,19 @@ function assessProfile(profile, evidence) {
|
|
|
3104
3145
|
});
|
|
3105
3146
|
continue;
|
|
3106
3147
|
}
|
|
3148
|
+
if (isDialogCountCheckType(check.type)) {
|
|
3149
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
3150
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
3151
|
+
const actualCount = numberValue(evidence.dom_summary && evidence.dom_summary[field]) ?? 0;
|
|
3152
|
+
checks.push({
|
|
3153
|
+
type: check.type,
|
|
3154
|
+
label: check.label || check.type,
|
|
3155
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
3156
|
+
evidence: { field, expected_count: expectedCount, count: actualCount },
|
|
3157
|
+
message: actualCount === expectedCount ? undefined : field + " did not equal " + expectedCount + "; observed " + actualCount + ".",
|
|
3158
|
+
});
|
|
3159
|
+
continue;
|
|
3160
|
+
}
|
|
3107
3161
|
if (check.type === "route_loaded") {
|
|
3108
3162
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
3109
3163
|
const failed = checkViewports.filter((viewport) => {
|
package/dist/cli.cjs
CHANGED
|
@@ -6575,6 +6575,9 @@ var RiddleApiError = class extends Error {
|
|
|
6575
6575
|
this.path = pathname;
|
|
6576
6576
|
}
|
|
6577
6577
|
};
|
|
6578
|
+
var PREVIEW_PUBLISH_RECOVERY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
6579
|
+
var PREVIEW_PUBLISH_RECOVERY_ATTEMPTS = 30;
|
|
6580
|
+
var PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS = 2e3;
|
|
6578
6581
|
function normalizeBaseUrl(value) {
|
|
6579
6582
|
return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
|
|
6580
6583
|
}
|
|
@@ -6609,6 +6612,45 @@ async function riddleRequestJson(config, pathname, init = {}) {
|
|
|
6609
6612
|
if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
|
|
6610
6613
|
return json ?? text;
|
|
6611
6614
|
}
|
|
6615
|
+
function previewDeployResultFromRecord(input) {
|
|
6616
|
+
const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
|
|
6617
|
+
return {
|
|
6618
|
+
ok: true,
|
|
6619
|
+
id: String(record.id || record.preview_id || id),
|
|
6620
|
+
label,
|
|
6621
|
+
framework,
|
|
6622
|
+
preview_url: String(record.preview_url || ""),
|
|
6623
|
+
file_count: typeof record.file_count === "number" ? record.file_count : void 0,
|
|
6624
|
+
total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
|
|
6625
|
+
expires_at: expiresAt,
|
|
6626
|
+
publish_recovered: publishRecovered || void 0,
|
|
6627
|
+
publish_error: publishError,
|
|
6628
|
+
raw: record
|
|
6629
|
+
};
|
|
6630
|
+
}
|
|
6631
|
+
function canRecoverPreviewPublish(error) {
|
|
6632
|
+
return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
|
|
6633
|
+
}
|
|
6634
|
+
async function waitForPublishedPreview(config, input) {
|
|
6635
|
+
for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
|
|
6636
|
+
const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
|
|
6637
|
+
if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
|
|
6638
|
+
return previewDeployResultFromRecord({
|
|
6639
|
+
record: status,
|
|
6640
|
+
id: input.id,
|
|
6641
|
+
label: input.label,
|
|
6642
|
+
framework: input.framework,
|
|
6643
|
+
expiresAt: input.expiresAt,
|
|
6644
|
+
publishRecovered: true,
|
|
6645
|
+
publishError: input.publishError.message
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6648
|
+
if (attempt < PREVIEW_PUBLISH_RECOVERY_ATTEMPTS) {
|
|
6649
|
+
await new Promise((resolve) => setTimeout(resolve, PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS));
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
throw input.publishError;
|
|
6653
|
+
}
|
|
6612
6654
|
async function deployRiddlePreview(config, directory, label, framework = "static") {
|
|
6613
6655
|
if (!directory?.trim()) throw new Error("directory is required");
|
|
6614
6656
|
if (!label?.trim()) throw new Error("label is required");
|
|
@@ -6635,20 +6677,22 @@ async function deployRiddlePreview(config, directory, label, framework = "static
|
|
|
6635
6677
|
} finally {
|
|
6636
6678
|
(0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
|
|
6637
6679
|
}
|
|
6638
|
-
const
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6680
|
+
const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
|
|
6681
|
+
try {
|
|
6682
|
+
const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
|
|
6683
|
+
method: "POST"
|
|
6684
|
+
});
|
|
6685
|
+
return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt });
|
|
6686
|
+
} catch (error) {
|
|
6687
|
+
if (!canRecoverPreviewPublish(error)) throw error;
|
|
6688
|
+
return waitForPublishedPreview(config, {
|
|
6689
|
+
id,
|
|
6690
|
+
label,
|
|
6691
|
+
framework,
|
|
6692
|
+
expiresAt,
|
|
6693
|
+
publishError: error
|
|
6694
|
+
});
|
|
6695
|
+
}
|
|
6652
6696
|
}
|
|
6653
6697
|
async function deployRiddleStaticPreview(config, directory, label) {
|
|
6654
6698
|
return deployRiddlePreview(config, directory, label, "static");
|
|
@@ -6913,6 +6957,9 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
6913
6957
|
"selector_count_equals",
|
|
6914
6958
|
"selector_count_equal",
|
|
6915
6959
|
"selector_count_eq",
|
|
6960
|
+
"dialog_count_equals",
|
|
6961
|
+
"dialog_accept_count_equals",
|
|
6962
|
+
"dialog_dismiss_count_equals",
|
|
6916
6963
|
"selector_text_visible",
|
|
6917
6964
|
"selector_text_absent",
|
|
6918
6965
|
"selector_text_order",
|
|
@@ -7665,6 +7712,14 @@ function validateRegexPatterns(patterns, label) {
|
|
|
7665
7712
|
}
|
|
7666
7713
|
}
|
|
7667
7714
|
}
|
|
7715
|
+
function isDialogCountCheckType(type) {
|
|
7716
|
+
return type === "dialog_count_equals" || type === "dialog_accept_count_equals" || type === "dialog_dismiss_count_equals";
|
|
7717
|
+
}
|
|
7718
|
+
function dialogCountFieldForCheckType(type) {
|
|
7719
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
7720
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
7721
|
+
return "dialog_count";
|
|
7722
|
+
}
|
|
7668
7723
|
function normalizeCheck(input, index) {
|
|
7669
7724
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
7670
7725
|
const type = stringValue2(input.type);
|
|
@@ -7672,6 +7727,7 @@ function normalizeCheck(input, index) {
|
|
|
7672
7727
|
if (!isSupportedCheckType(type)) {
|
|
7673
7728
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
7674
7729
|
}
|
|
7730
|
+
const isDialogCountCheck = isDialogCountCheckType(type);
|
|
7675
7731
|
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue2(input.selector)) {
|
|
7676
7732
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
7677
7733
|
}
|
|
@@ -7703,9 +7759,12 @@ function normalizeCheck(input, index) {
|
|
|
7703
7759
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
7704
7760
|
}
|
|
7705
7761
|
const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
|
|
7706
|
-
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
7762
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || isDialogCountCheck) && expectedCount === void 0) {
|
|
7707
7763
|
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
7708
7764
|
}
|
|
7765
|
+
if (isDialogCountCheck && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
|
|
7766
|
+
throw new Error(`checks[${index}] ${type} expected_count must be a non-negative integer.`);
|
|
7767
|
+
}
|
|
7709
7768
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
7710
7769
|
if (type === "selector_text_order") {
|
|
7711
7770
|
if (!stringValue2(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -8375,6 +8434,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
8375
8434
|
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
8376
8435
|
};
|
|
8377
8436
|
}
|
|
8437
|
+
if (isDialogCountCheckType(check.type)) {
|
|
8438
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
8439
|
+
const expectedCount = check.expected_count ?? 0;
|
|
8440
|
+
const actualCount = numberValue(evidence.dom_summary?.[field]) ?? 0;
|
|
8441
|
+
return {
|
|
8442
|
+
type: check.type,
|
|
8443
|
+
label: checkLabel(check),
|
|
8444
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
8445
|
+
evidence: {
|
|
8446
|
+
field,
|
|
8447
|
+
expected_count: expectedCount,
|
|
8448
|
+
count: actualCount
|
|
8449
|
+
},
|
|
8450
|
+
message: actualCount === expectedCount ? void 0 : `${field} did not equal ${expectedCount}; observed ${actualCount}.`
|
|
8451
|
+
};
|
|
8452
|
+
}
|
|
8378
8453
|
if (check.type === "route_loaded") {
|
|
8379
8454
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
8380
8455
|
const failed = viewports.filter((viewport) => !successfulRoute({
|
|
@@ -9610,6 +9685,16 @@ function summarizeRouteInventory(viewport, inventory) {
|
|
|
9610
9685
|
function numberValue(value) {
|
|
9611
9686
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
9612
9687
|
}
|
|
9688
|
+
function isDialogCountCheckType(type) {
|
|
9689
|
+
return type === "dialog_count_equals"
|
|
9690
|
+
|| type === "dialog_accept_count_equals"
|
|
9691
|
+
|| type === "dialog_dismiss_count_equals";
|
|
9692
|
+
}
|
|
9693
|
+
function dialogCountFieldForCheckType(type) {
|
|
9694
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
9695
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
9696
|
+
return "dialog_count";
|
|
9697
|
+
}
|
|
9613
9698
|
function maxPositiveNumber() {
|
|
9614
9699
|
let max = 0;
|
|
9615
9700
|
for (const value of arguments) {
|
|
@@ -9981,6 +10066,19 @@ function assessProfile(profile, evidence) {
|
|
|
9981
10066
|
});
|
|
9982
10067
|
continue;
|
|
9983
10068
|
}
|
|
10069
|
+
if (isDialogCountCheckType(check.type)) {
|
|
10070
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
10071
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
10072
|
+
const actualCount = numberValue(evidence.dom_summary && evidence.dom_summary[field]) ?? 0;
|
|
10073
|
+
checks.push({
|
|
10074
|
+
type: check.type,
|
|
10075
|
+
label: check.label || check.type,
|
|
10076
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
10077
|
+
evidence: { field, expected_count: expectedCount, count: actualCount },
|
|
10078
|
+
message: actualCount === expectedCount ? undefined : field + " did not equal " + expectedCount + "; observed " + actualCount + ".",
|
|
10079
|
+
});
|
|
10080
|
+
continue;
|
|
10081
|
+
}
|
|
9984
10082
|
if (check.type === "route_loaded") {
|
|
9985
10083
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
9986
10084
|
const failed = checkViewports.filter((viewport) => {
|
package/dist/cli.js
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
profileStatusExitCode,
|
|
11
11
|
resolveRiddleProofProfileTargetUrl,
|
|
12
12
|
resolveRiddleProofProfileTimeoutSec
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-QOOPPRYK.js";
|
|
14
14
|
import {
|
|
15
15
|
createRiddleApiClient,
|
|
16
16
|
parseRiddleViewport
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-7GHBRZHQ.js";
|
|
18
18
|
import {
|
|
19
19
|
createDisabledRiddleProofAgentAdapter,
|
|
20
20
|
readRiddleProofRunStatus,
|
package/dist/index.cjs
CHANGED
|
@@ -8750,6 +8750,9 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
8750
8750
|
"selector_count_equals",
|
|
8751
8751
|
"selector_count_equal",
|
|
8752
8752
|
"selector_count_eq",
|
|
8753
|
+
"dialog_count_equals",
|
|
8754
|
+
"dialog_accept_count_equals",
|
|
8755
|
+
"dialog_dismiss_count_equals",
|
|
8753
8756
|
"selector_text_visible",
|
|
8754
8757
|
"selector_text_absent",
|
|
8755
8758
|
"selector_text_order",
|
|
@@ -9502,6 +9505,14 @@ function validateRegexPatterns(patterns, label) {
|
|
|
9502
9505
|
}
|
|
9503
9506
|
}
|
|
9504
9507
|
}
|
|
9508
|
+
function isDialogCountCheckType(type) {
|
|
9509
|
+
return type === "dialog_count_equals" || type === "dialog_accept_count_equals" || type === "dialog_dismiss_count_equals";
|
|
9510
|
+
}
|
|
9511
|
+
function dialogCountFieldForCheckType(type) {
|
|
9512
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
9513
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
9514
|
+
return "dialog_count";
|
|
9515
|
+
}
|
|
9505
9516
|
function normalizeCheck(input, index) {
|
|
9506
9517
|
if (!isRecord2(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
9507
9518
|
const type = stringValue5(input.type);
|
|
@@ -9509,6 +9520,7 @@ function normalizeCheck(input, index) {
|
|
|
9509
9520
|
if (!isSupportedCheckType(type)) {
|
|
9510
9521
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
9511
9522
|
}
|
|
9523
|
+
const isDialogCountCheck = isDialogCountCheckType(type);
|
|
9512
9524
|
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue5(input.selector)) {
|
|
9513
9525
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
9514
9526
|
}
|
|
@@ -9540,9 +9552,12 @@ function normalizeCheck(input, index) {
|
|
|
9540
9552
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
9541
9553
|
}
|
|
9542
9554
|
const expectedCount = numberValue3(input.expected_count) ?? numberValue3(input.expectedCount) ?? numberValue3(input.count);
|
|
9543
|
-
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
9555
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || isDialogCountCheck) && expectedCount === void 0) {
|
|
9544
9556
|
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
9545
9557
|
}
|
|
9558
|
+
if (isDialogCountCheck && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
|
|
9559
|
+
throw new Error(`checks[${index}] ${type} expected_count must be a non-negative integer.`);
|
|
9560
|
+
}
|
|
9546
9561
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
9547
9562
|
if (type === "selector_text_order") {
|
|
9548
9563
|
if (!stringValue5(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -10212,6 +10227,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
10212
10227
|
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
10213
10228
|
};
|
|
10214
10229
|
}
|
|
10230
|
+
if (isDialogCountCheckType(check.type)) {
|
|
10231
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
10232
|
+
const expectedCount = check.expected_count ?? 0;
|
|
10233
|
+
const actualCount = numberValue3(evidence.dom_summary?.[field]) ?? 0;
|
|
10234
|
+
return {
|
|
10235
|
+
type: check.type,
|
|
10236
|
+
label: checkLabel(check),
|
|
10237
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
10238
|
+
evidence: {
|
|
10239
|
+
field,
|
|
10240
|
+
expected_count: expectedCount,
|
|
10241
|
+
count: actualCount
|
|
10242
|
+
},
|
|
10243
|
+
message: actualCount === expectedCount ? void 0 : `${field} did not equal ${expectedCount}; observed ${actualCount}.`
|
|
10244
|
+
};
|
|
10245
|
+
}
|
|
10215
10246
|
if (check.type === "route_loaded") {
|
|
10216
10247
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
10217
10248
|
const failed = viewports.filter((viewport) => !successfulRoute({
|
|
@@ -11463,6 +11494,16 @@ function summarizeRouteInventory(viewport, inventory) {
|
|
|
11463
11494
|
function numberValue(value) {
|
|
11464
11495
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
11465
11496
|
}
|
|
11497
|
+
function isDialogCountCheckType(type) {
|
|
11498
|
+
return type === "dialog_count_equals"
|
|
11499
|
+
|| type === "dialog_accept_count_equals"
|
|
11500
|
+
|| type === "dialog_dismiss_count_equals";
|
|
11501
|
+
}
|
|
11502
|
+
function dialogCountFieldForCheckType(type) {
|
|
11503
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
11504
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
11505
|
+
return "dialog_count";
|
|
11506
|
+
}
|
|
11466
11507
|
function maxPositiveNumber() {
|
|
11467
11508
|
let max = 0;
|
|
11468
11509
|
for (const value of arguments) {
|
|
@@ -11834,6 +11875,19 @@ function assessProfile(profile, evidence) {
|
|
|
11834
11875
|
});
|
|
11835
11876
|
continue;
|
|
11836
11877
|
}
|
|
11878
|
+
if (isDialogCountCheckType(check.type)) {
|
|
11879
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
11880
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
11881
|
+
const actualCount = numberValue(evidence.dom_summary && evidence.dom_summary[field]) ?? 0;
|
|
11882
|
+
checks.push({
|
|
11883
|
+
type: check.type,
|
|
11884
|
+
label: check.label || check.type,
|
|
11885
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
11886
|
+
evidence: { field, expected_count: expectedCount, count: actualCount },
|
|
11887
|
+
message: actualCount === expectedCount ? undefined : field + " did not equal " + expectedCount + "; observed " + actualCount + ".",
|
|
11888
|
+
});
|
|
11889
|
+
continue;
|
|
11890
|
+
}
|
|
11837
11891
|
if (check.type === "route_loaded") {
|
|
11838
11892
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
11839
11893
|
const failed = checkViewports.filter((viewport) => {
|
|
@@ -14476,6 +14530,9 @@ var RiddleApiError = class extends Error {
|
|
|
14476
14530
|
this.path = pathname;
|
|
14477
14531
|
}
|
|
14478
14532
|
};
|
|
14533
|
+
var PREVIEW_PUBLISH_RECOVERY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
14534
|
+
var PREVIEW_PUBLISH_RECOVERY_ATTEMPTS = 30;
|
|
14535
|
+
var PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS = 2e3;
|
|
14479
14536
|
function normalizeBaseUrl(value) {
|
|
14480
14537
|
return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
|
|
14481
14538
|
}
|
|
@@ -14510,6 +14567,45 @@ async function riddleRequestJson(config, pathname, init = {}) {
|
|
|
14510
14567
|
if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
|
|
14511
14568
|
return json ?? text;
|
|
14512
14569
|
}
|
|
14570
|
+
function previewDeployResultFromRecord(input) {
|
|
14571
|
+
const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
|
|
14572
|
+
return {
|
|
14573
|
+
ok: true,
|
|
14574
|
+
id: String(record.id || record.preview_id || id),
|
|
14575
|
+
label,
|
|
14576
|
+
framework,
|
|
14577
|
+
preview_url: String(record.preview_url || ""),
|
|
14578
|
+
file_count: typeof record.file_count === "number" ? record.file_count : void 0,
|
|
14579
|
+
total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
|
|
14580
|
+
expires_at: expiresAt,
|
|
14581
|
+
publish_recovered: publishRecovered || void 0,
|
|
14582
|
+
publish_error: publishError,
|
|
14583
|
+
raw: record
|
|
14584
|
+
};
|
|
14585
|
+
}
|
|
14586
|
+
function canRecoverPreviewPublish(error) {
|
|
14587
|
+
return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
|
|
14588
|
+
}
|
|
14589
|
+
async function waitForPublishedPreview(config, input) {
|
|
14590
|
+
for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
|
|
14591
|
+
const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
|
|
14592
|
+
if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
|
|
14593
|
+
return previewDeployResultFromRecord({
|
|
14594
|
+
record: status,
|
|
14595
|
+
id: input.id,
|
|
14596
|
+
label: input.label,
|
|
14597
|
+
framework: input.framework,
|
|
14598
|
+
expiresAt: input.expiresAt,
|
|
14599
|
+
publishRecovered: true,
|
|
14600
|
+
publishError: input.publishError.message
|
|
14601
|
+
});
|
|
14602
|
+
}
|
|
14603
|
+
if (attempt < PREVIEW_PUBLISH_RECOVERY_ATTEMPTS) {
|
|
14604
|
+
await new Promise((resolve) => setTimeout(resolve, PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS));
|
|
14605
|
+
}
|
|
14606
|
+
}
|
|
14607
|
+
throw input.publishError;
|
|
14608
|
+
}
|
|
14513
14609
|
async function deployRiddlePreview(config, directory, label, framework = "static") {
|
|
14514
14610
|
if (!directory?.trim()) throw new Error("directory is required");
|
|
14515
14611
|
if (!label?.trim()) throw new Error("label is required");
|
|
@@ -14536,20 +14632,22 @@ async function deployRiddlePreview(config, directory, label, framework = "static
|
|
|
14536
14632
|
} finally {
|
|
14537
14633
|
(0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
|
|
14538
14634
|
}
|
|
14539
|
-
const
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14635
|
+
const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
|
|
14636
|
+
try {
|
|
14637
|
+
const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
|
|
14638
|
+
method: "POST"
|
|
14639
|
+
});
|
|
14640
|
+
return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt });
|
|
14641
|
+
} catch (error) {
|
|
14642
|
+
if (!canRecoverPreviewPublish(error)) throw error;
|
|
14643
|
+
return waitForPublishedPreview(config, {
|
|
14644
|
+
id,
|
|
14645
|
+
label,
|
|
14646
|
+
framework,
|
|
14647
|
+
expiresAt,
|
|
14648
|
+
publishError: error
|
|
14649
|
+
});
|
|
14650
|
+
}
|
|
14553
14651
|
}
|
|
14554
14652
|
async function deployRiddleStaticPreview(config, directory, label) {
|
|
14555
14653
|
return deployRiddlePreview(config, directory, label, "static");
|
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
resolveRiddleProofProfileTimeoutSec,
|
|
60
60
|
slugifyRiddleProofProfileName,
|
|
61
61
|
summarizeRiddleProofProfileResult
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-QOOPPRYK.js";
|
|
63
63
|
import {
|
|
64
64
|
DEFAULT_RIDDLE_API_BASE_URL,
|
|
65
65
|
DEFAULT_RIDDLE_API_KEY_FILE,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
riddleRequestJson,
|
|
75
75
|
runRiddleScript,
|
|
76
76
|
runRiddleServerPreview
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-7GHBRZHQ.js";
|
|
78
78
|
import {
|
|
79
79
|
DEFAULT_DIAGNOSTIC_ARRAY_LIMIT,
|
|
80
80
|
DEFAULT_DIAGNOSTIC_HISTORY_LIMIT,
|
package/dist/profile.cjs
CHANGED
|
@@ -64,6 +64,9 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
64
64
|
"selector_count_equals",
|
|
65
65
|
"selector_count_equal",
|
|
66
66
|
"selector_count_eq",
|
|
67
|
+
"dialog_count_equals",
|
|
68
|
+
"dialog_accept_count_equals",
|
|
69
|
+
"dialog_dismiss_count_equals",
|
|
67
70
|
"selector_text_visible",
|
|
68
71
|
"selector_text_absent",
|
|
69
72
|
"selector_text_order",
|
|
@@ -816,6 +819,14 @@ function validateRegexPatterns(patterns, label) {
|
|
|
816
819
|
}
|
|
817
820
|
}
|
|
818
821
|
}
|
|
822
|
+
function isDialogCountCheckType(type) {
|
|
823
|
+
return type === "dialog_count_equals" || type === "dialog_accept_count_equals" || type === "dialog_dismiss_count_equals";
|
|
824
|
+
}
|
|
825
|
+
function dialogCountFieldForCheckType(type) {
|
|
826
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
827
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
828
|
+
return "dialog_count";
|
|
829
|
+
}
|
|
819
830
|
function normalizeCheck(input, index) {
|
|
820
831
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
821
832
|
const type = stringValue(input.type);
|
|
@@ -823,6 +834,7 @@ function normalizeCheck(input, index) {
|
|
|
823
834
|
if (!isSupportedCheckType(type)) {
|
|
824
835
|
throw new Error(`checks[${index}].type ${type} is not supported. Supported checks: ${RIDDLE_PROOF_PROFILE_CHECK_TYPES.join(", ")}`);
|
|
825
836
|
}
|
|
837
|
+
const isDialogCountCheck = isDialogCountCheckType(type);
|
|
826
838
|
if ((type === "selector_visible" || type === "selector_absent" || type === "selector_count_at_least" || type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || type === "selector_text_visible" || type === "selector_text_absent") && !stringValue(input.selector)) {
|
|
827
839
|
throw new Error(`checks[${index}] ${type} requires selector.`);
|
|
828
840
|
}
|
|
@@ -854,9 +866,12 @@ function normalizeCheck(input, index) {
|
|
|
854
866
|
throw new Error(`checks[${index}] selector_count_at_least requires min_count.`);
|
|
855
867
|
}
|
|
856
868
|
const expectedCount = numberValue(input.expected_count) ?? numberValue(input.expectedCount) ?? numberValue(input.count);
|
|
857
|
-
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq") && expectedCount === void 0) {
|
|
869
|
+
if ((type === "selector_count_equals" || type === "selector_count_equal" || type === "selector_count_eq" || isDialogCountCheck) && expectedCount === void 0) {
|
|
858
870
|
throw new Error(`checks[${index}] ${type} requires expected_count.`);
|
|
859
871
|
}
|
|
872
|
+
if (isDialogCountCheck && (expectedCount === void 0 || !Number.isInteger(expectedCount) || expectedCount < 0)) {
|
|
873
|
+
throw new Error(`checks[${index}] ${type} expected_count must be a non-negative integer.`);
|
|
874
|
+
}
|
|
860
875
|
const expectedTexts = normalizeExpectedTexts(input.expected_texts ?? input.expectedTexts, index);
|
|
861
876
|
if (type === "selector_text_order") {
|
|
862
877
|
if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
@@ -1526,6 +1541,22 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
1526
1541
|
message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
|
|
1527
1542
|
};
|
|
1528
1543
|
}
|
|
1544
|
+
if (isDialogCountCheckType(check.type)) {
|
|
1545
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
1546
|
+
const expectedCount = check.expected_count ?? 0;
|
|
1547
|
+
const actualCount = numberValue(evidence.dom_summary?.[field]) ?? 0;
|
|
1548
|
+
return {
|
|
1549
|
+
type: check.type,
|
|
1550
|
+
label: checkLabel(check),
|
|
1551
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
1552
|
+
evidence: {
|
|
1553
|
+
field,
|
|
1554
|
+
expected_count: expectedCount,
|
|
1555
|
+
count: actualCount
|
|
1556
|
+
},
|
|
1557
|
+
message: actualCount === expectedCount ? void 0 : `${field} did not equal ${expectedCount}; observed ${actualCount}.`
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1529
1560
|
if (check.type === "route_loaded") {
|
|
1530
1561
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
1531
1562
|
const failed = viewports.filter((viewport) => !successfulRoute({
|
|
@@ -2777,6 +2808,16 @@ function summarizeRouteInventory(viewport, inventory) {
|
|
|
2777
2808
|
function numberValue(value) {
|
|
2778
2809
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
2779
2810
|
}
|
|
2811
|
+
function isDialogCountCheckType(type) {
|
|
2812
|
+
return type === "dialog_count_equals"
|
|
2813
|
+
|| type === "dialog_accept_count_equals"
|
|
2814
|
+
|| type === "dialog_dismiss_count_equals";
|
|
2815
|
+
}
|
|
2816
|
+
function dialogCountFieldForCheckType(type) {
|
|
2817
|
+
if (type === "dialog_accept_count_equals") return "dialog_accept_count";
|
|
2818
|
+
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
2819
|
+
return "dialog_count";
|
|
2820
|
+
}
|
|
2780
2821
|
function maxPositiveNumber() {
|
|
2781
2822
|
let max = 0;
|
|
2782
2823
|
for (const value of arguments) {
|
|
@@ -3148,6 +3189,19 @@ function assessProfile(profile, evidence) {
|
|
|
3148
3189
|
});
|
|
3149
3190
|
continue;
|
|
3150
3191
|
}
|
|
3192
|
+
if (isDialogCountCheckType(check.type)) {
|
|
3193
|
+
const field = dialogCountFieldForCheckType(check.type);
|
|
3194
|
+
const expectedCount = check.expected_count == null ? 0 : check.expected_count;
|
|
3195
|
+
const actualCount = numberValue(evidence.dom_summary && evidence.dom_summary[field]) ?? 0;
|
|
3196
|
+
checks.push({
|
|
3197
|
+
type: check.type,
|
|
3198
|
+
label: check.label || check.type,
|
|
3199
|
+
status: actualCount === expectedCount ? "passed" : "failed",
|
|
3200
|
+
evidence: { field, expected_count: expectedCount, count: actualCount },
|
|
3201
|
+
message: actualCount === expectedCount ? undefined : field + " did not equal " + expectedCount + "; observed " + actualCount + ".",
|
|
3202
|
+
});
|
|
3203
|
+
continue;
|
|
3204
|
+
}
|
|
3151
3205
|
if (check.type === "route_loaded") {
|
|
3152
3206
|
const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
|
|
3153
3207
|
const failed = checkViewports.filter((viewport) => {
|
package/dist/profile.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "dialog_count_equals", "dialog_accept_count_equals", "dialog_dismiss_count_equals", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
package/dist/profile.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare const RIDDLE_PROOF_PROFILE_VERSION: "riddle-proof.profile.v1";
|
|
|
4
4
|
declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evidence.v1";
|
|
5
5
|
declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
|
|
6
6
|
declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
|
|
7
|
-
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
|
|
7
|
+
declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "dialog_count_equals", "dialog_accept_count_equals", "dialog_dismiss_count_equals", "selector_text_visible", "selector_text_absent", "selector_text_order", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
|
|
8
8
|
declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "drag", "press", "fill", "set_input_value", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_call"];
|
|
9
9
|
type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
|
|
10
10
|
type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
|
package/dist/profile.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
resolveRiddleProofProfileTimeoutSec,
|
|
21
21
|
slugifyRiddleProofProfileName,
|
|
22
22
|
summarizeRiddleProofProfileResult
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-QOOPPRYK.js";
|
|
24
24
|
export {
|
|
25
25
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
26
26
|
RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|
|
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
292
292
|
blocking?: boolean;
|
|
293
293
|
details?: Record<string, unknown>;
|
|
294
294
|
ok: boolean;
|
|
295
|
-
action: "
|
|
295
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
296
296
|
state_path: string;
|
|
297
297
|
stage: any;
|
|
298
298
|
summary: string;
|
|
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
382
382
|
continueWithStage?: WorkflowStage | null;
|
|
383
383
|
blocking?: boolean;
|
|
384
384
|
details?: Record<string, unknown>;
|
|
385
|
-
action: "
|
|
385
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
386
386
|
state_path: string;
|
|
387
387
|
stage: any;
|
|
388
388
|
checkpoint: string;
|
|
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
|
|
|
659
659
|
error?: undefined;
|
|
660
660
|
} | {
|
|
661
661
|
ok: boolean;
|
|
662
|
-
action: "
|
|
662
|
+
action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
|
|
663
663
|
state_path: string;
|
|
664
664
|
stage: any;
|
|
665
665
|
summary: string;
|
package/dist/riddle-client.cjs
CHANGED
|
@@ -60,6 +60,9 @@ var RiddleApiError = class extends Error {
|
|
|
60
60
|
this.path = pathname;
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
+
var PREVIEW_PUBLISH_RECOVERY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
64
|
+
var PREVIEW_PUBLISH_RECOVERY_ATTEMPTS = 30;
|
|
65
|
+
var PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS = 2e3;
|
|
63
66
|
function normalizeBaseUrl(value) {
|
|
64
67
|
return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
|
|
65
68
|
}
|
|
@@ -94,6 +97,45 @@ async function riddleRequestJson(config, pathname, init = {}) {
|
|
|
94
97
|
if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
|
|
95
98
|
return json ?? text;
|
|
96
99
|
}
|
|
100
|
+
function previewDeployResultFromRecord(input) {
|
|
101
|
+
const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
|
|
102
|
+
return {
|
|
103
|
+
ok: true,
|
|
104
|
+
id: String(record.id || record.preview_id || id),
|
|
105
|
+
label,
|
|
106
|
+
framework,
|
|
107
|
+
preview_url: String(record.preview_url || ""),
|
|
108
|
+
file_count: typeof record.file_count === "number" ? record.file_count : void 0,
|
|
109
|
+
total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
|
|
110
|
+
expires_at: expiresAt,
|
|
111
|
+
publish_recovered: publishRecovered || void 0,
|
|
112
|
+
publish_error: publishError,
|
|
113
|
+
raw: record
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function canRecoverPreviewPublish(error) {
|
|
117
|
+
return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
|
|
118
|
+
}
|
|
119
|
+
async function waitForPublishedPreview(config, input) {
|
|
120
|
+
for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
|
|
121
|
+
const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
|
|
122
|
+
if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
|
|
123
|
+
return previewDeployResultFromRecord({
|
|
124
|
+
record: status,
|
|
125
|
+
id: input.id,
|
|
126
|
+
label: input.label,
|
|
127
|
+
framework: input.framework,
|
|
128
|
+
expiresAt: input.expiresAt,
|
|
129
|
+
publishRecovered: true,
|
|
130
|
+
publishError: input.publishError.message
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (attempt < PREVIEW_PUBLISH_RECOVERY_ATTEMPTS) {
|
|
134
|
+
await new Promise((resolve) => setTimeout(resolve, PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
throw input.publishError;
|
|
138
|
+
}
|
|
97
139
|
async function deployRiddlePreview(config, directory, label, framework = "static") {
|
|
98
140
|
if (!directory?.trim()) throw new Error("directory is required");
|
|
99
141
|
if (!label?.trim()) throw new Error("label is required");
|
|
@@ -120,20 +162,22 @@ async function deployRiddlePreview(config, directory, label, framework = "static
|
|
|
120
162
|
} finally {
|
|
121
163
|
(0, import_node_fs.rmSync)(scratch, { recursive: true, force: true });
|
|
122
164
|
}
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
165
|
+
const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
|
|
166
|
+
try {
|
|
167
|
+
const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
|
|
168
|
+
method: "POST"
|
|
169
|
+
});
|
|
170
|
+
return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (!canRecoverPreviewPublish(error)) throw error;
|
|
173
|
+
return waitForPublishedPreview(config, {
|
|
174
|
+
id,
|
|
175
|
+
label,
|
|
176
|
+
framework,
|
|
177
|
+
expiresAt,
|
|
178
|
+
publishError: error
|
|
179
|
+
});
|
|
180
|
+
}
|
|
137
181
|
}
|
|
138
182
|
async function deployRiddleStaticPreview(config, directory, label) {
|
|
139
183
|
return deployRiddlePreview(config, directory, label, "static");
|
package/dist/riddle-client.d.cts
CHANGED
package/dist/riddle-client.d.ts
CHANGED
package/dist/riddle-client.js
CHANGED