@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 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 published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
78
- method: "POST"
79
- });
80
- return {
81
- ok: true,
82
- id: String(published.id || id),
83
- label,
84
- framework,
85
- preview_url: String(published.preview_url || ""),
86
- file_count: typeof published.file_count === "number" ? published.file_count : void 0,
87
- total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : void 0,
88
- expires_at: typeof created.expires_at === "string" ? created.expires_at : void 0,
89
- raw: published
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 published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
6639
- method: "POST"
6640
- });
6641
- return {
6642
- ok: true,
6643
- id: String(published.id || id),
6644
- label,
6645
- framework,
6646
- preview_url: String(published.preview_url || ""),
6647
- file_count: typeof published.file_count === "number" ? published.file_count : void 0,
6648
- total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : void 0,
6649
- expires_at: typeof created.expires_at === "string" ? created.expires_at : void 0,
6650
- raw: published
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-3CND75DM.js";
13
+ } from "./chunk-QOOPPRYK.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
17
- } from "./chunk-OI57LO6Y.js";
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 published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
14540
- method: "POST"
14541
- });
14542
- return {
14543
- ok: true,
14544
- id: String(published.id || id),
14545
- label,
14546
- framework,
14547
- preview_url: String(published.preview_url || ""),
14548
- file_count: typeof published.file_count === "number" ? published.file_count : void 0,
14549
- total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : void 0,
14550
- expires_at: typeof created.expires_at === "string" ? created.expires_at : void 0,
14551
- raw: published
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-3CND75DM.js";
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-OI57LO6Y.js";
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) => {
@@ -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-3CND75DM.js";
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: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
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: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
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: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
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: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
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: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
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: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
662
+ action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
@@ -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 published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
124
- method: "POST"
125
- });
126
- return {
127
- ok: true,
128
- id: String(published.id || id),
129
- label,
130
- framework,
131
- preview_url: String(published.preview_url || ""),
132
- file_count: typeof published.file_count === "number" ? published.file_count : void 0,
133
- total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : void 0,
134
- expires_at: typeof created.expires_at === "string" ? created.expires_at : void 0,
135
- raw: published
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");
@@ -42,6 +42,8 @@ interface RiddlePreviewDeployResult {
42
42
  file_count?: number;
43
43
  total_bytes?: number;
44
44
  expires_at?: string;
45
+ publish_recovered?: boolean;
46
+ publish_error?: string;
45
47
  raw?: Record<string, unknown>;
46
48
  }
47
49
  interface RiddleRunScriptInput {
@@ -42,6 +42,8 @@ interface RiddlePreviewDeployResult {
42
42
  file_count?: number;
43
43
  total_bytes?: number;
44
44
  expires_at?: string;
45
+ publish_recovered?: boolean;
46
+ publish_error?: string;
45
47
  raw?: Record<string, unknown>;
46
48
  }
47
49
  interface RiddleRunScriptInput {
@@ -12,7 +12,7 @@ import {
12
12
  riddleRequestJson,
13
13
  runRiddleScript,
14
14
  runRiddleServerPreview
15
- } from "./chunk-OI57LO6Y.js";
15
+ } from "./chunk-7GHBRZHQ.js";
16
16
  export {
17
17
  DEFAULT_RIDDLE_API_BASE_URL,
18
18
  DEFAULT_RIDDLE_API_KEY_FILE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.113",
3
+ "version": "0.7.115",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",