@riddledc/riddle-proof 0.7.44 → 0.7.45

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
@@ -223,6 +223,13 @@ cycle instead of reusing the final response, for example to repeat a fail-then-
223
223
  success pair across multiple viewports. Repeated sequences also record
224
224
  `sequence_cycle: true` after the first cycle.
225
225
 
226
+ Set `capture_request_body: true` to include compact request-body evidence on
227
+ mock hits. Add `request_body_contains` or `request_body_patterns` when the
228
+ request body is part of the contract, such as proving that a save request
229
+ references the build ID returned by a prior mocked build response. Body
230
+ assertions use the full request body for matching and store only length plus a
231
+ compact sample in the proof evidence.
232
+
226
233
  `target.setup_actions` is optional. Use it when the meaningful proof surface
227
234
  appears only after a picker, tab, login stub, storage seed, form fill,
228
235
  transport control, or other bounded interaction. Supported setup actions are
@@ -253,6 +253,15 @@ function normalizeNetworkMock(input, index) {
253
253
  const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
254
254
  const responsesInput = input.responses ?? input.sequence;
255
255
  const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
256
+ const requestBodyContains = normalizeStringList(
257
+ input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
258
+ `target.network_mocks[${index}].request_body_contains`
259
+ );
260
+ const requestBodyPatterns = normalizeStringList(
261
+ input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
262
+ `target.network_mocks[${index}].request_body_patterns`
263
+ );
264
+ validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
256
265
  const requiredHitCount = numberValue(
257
266
  input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
258
267
  );
@@ -267,7 +276,10 @@ function normalizeNetworkMock(input, index) {
267
276
  responses,
268
277
  repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
269
278
  required_hit_count: requiredHitCount,
270
- required: input.required === false ? false : true
279
+ required: input.required === false ? false : true,
280
+ capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length),
281
+ request_body_contains: requestBodyContains,
282
+ request_body_patterns: requestBodyPatterns
271
283
  };
272
284
  }
273
285
  function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
@@ -350,6 +362,16 @@ function normalizeStringList(value, label) {
350
362
  if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
351
363
  return values;
352
364
  }
365
+ function validateRegexPatterns(patterns, label) {
366
+ for (const pattern of patterns || []) {
367
+ try {
368
+ new RegExp(pattern);
369
+ } catch (error) {
370
+ const message = error instanceof Error ? error.message : String(error);
371
+ throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
372
+ }
373
+ }
374
+ }
353
375
  function normalizeCheck(input, index) {
354
376
  if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
355
377
  const type = stringValue(input.type);
@@ -1020,6 +1042,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
1020
1042
  reason: event.reason ?? event.error ?? "mock_failed"
1021
1043
  });
1022
1044
  }
1045
+ if (event.request_body_matches === false) {
1046
+ failed.push({
1047
+ label: event.label ?? null,
1048
+ url: event.url ?? null,
1049
+ method: event.method ?? null,
1050
+ reason: "request_body_mismatch",
1051
+ request_body_failures: event.request_body_failures ?? [],
1052
+ request_body_length: event.request_body_length ?? null,
1053
+ request_body_sample: event.request_body_sample ?? null
1054
+ });
1055
+ }
1023
1056
  }
1024
1057
  return {
1025
1058
  type: "network_mocks_succeeded",
@@ -1392,6 +1425,17 @@ function assessProfile(profile, evidence) {
1392
1425
  reason: event.reason || event.error || "mock_failed",
1393
1426
  });
1394
1427
  }
1428
+ if (event && event.request_body_matches === false) {
1429
+ failed.push({
1430
+ label: event.label || null,
1431
+ url: event.url || null,
1432
+ method: event.method || null,
1433
+ reason: "request_body_mismatch",
1434
+ request_body_failures: event.request_body_failures || [],
1435
+ request_body_length: event.request_body_length || null,
1436
+ request_body_sample: event.request_body_sample || null,
1437
+ });
1438
+ }
1395
1439
  }
1396
1440
  const hitsByLabel = {};
1397
1441
  const requiredHitsByLabel = {};
@@ -1840,6 +1884,52 @@ function compactSetupResultText(value) {
1840
1884
  if (text.length <= 500) return text;
1841
1885
  return text.slice(0, 500) + "... (" + text.length + " chars)";
1842
1886
  }
1887
+ function compactNetworkMockRequestBody(value) {
1888
+ const text = String(value || "").replace(/\s+/g, " ").trim();
1889
+ if (text.length <= 1000) return text;
1890
+ return text.slice(0, 1000) + "... (" + text.length + " chars)";
1891
+ }
1892
+ function networkMockStringList(value) {
1893
+ return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
1894
+ }
1895
+ function networkMockShouldCaptureRequestBody(mock) {
1896
+ return mock && (
1897
+ mock.capture_request_body === true
1898
+ || networkMockStringList(mock.request_body_contains).length > 0
1899
+ || networkMockStringList(mock.request_body_patterns).length > 0
1900
+ );
1901
+ }
1902
+ function networkMockRequestBodyFailures(body, mock) {
1903
+ const failures = [];
1904
+ const rawBody = String(body || "");
1905
+ const compactBody = compactNetworkMockRequestBody(rawBody);
1906
+ for (const expected of networkMockStringList(mock.request_body_contains)) {
1907
+ if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
1908
+ failures.push({
1909
+ type: "request_body_missing_text",
1910
+ text: String(expected).slice(0, 200),
1911
+ });
1912
+ }
1913
+ }
1914
+ for (const pattern of networkMockStringList(mock.request_body_patterns)) {
1915
+ try {
1916
+ const regex = new RegExp(pattern);
1917
+ if (!regex.test(rawBody) && !regex.test(compactBody)) {
1918
+ failures.push({
1919
+ type: "request_body_pattern_not_matched",
1920
+ pattern: String(pattern).slice(0, 200),
1921
+ });
1922
+ }
1923
+ } catch (error) {
1924
+ failures.push({
1925
+ type: "request_body_invalid_pattern",
1926
+ pattern: String(pattern).slice(0, 200),
1927
+ error: String(error && error.message ? error.message : error).slice(0, 500),
1928
+ });
1929
+ }
1930
+ }
1931
+ return failures;
1932
+ }
1843
1933
  async function setupLocatorVisible(locator, index) {
1844
1934
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
1845
1935
  }
@@ -1868,7 +1958,11 @@ async function registerNetworkMocks(mocks) {
1868
1958
  body = JSON.stringify(response.body_json);
1869
1959
  contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
1870
1960
  }
1871
- networkMockEvents.push({
1961
+ const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
1962
+ const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
1963
+ const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
1964
+ const status = response.status || mock.status || 200;
1965
+ const event = {
1872
1966
  ok: true,
1873
1967
  label: mock.label,
1874
1968
  response_label: response.label || null,
@@ -1878,10 +1972,17 @@ async function registerNetworkMocks(mocks) {
1878
1972
  sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
1879
1973
  url: request.url(),
1880
1974
  method,
1881
- status: response.status || mock.status || 200,
1882
- });
1975
+ status,
1976
+ };
1977
+ if (shouldCaptureRequestBody) {
1978
+ event.request_body_matches = requestBodyFailures.length === 0;
1979
+ event.request_body_failures = requestBodyFailures;
1980
+ event.request_body_length = requestBody.length;
1981
+ event.request_body_sample = compactNetworkMockRequestBody(requestBody);
1982
+ }
1983
+ networkMockEvents.push(event);
1883
1984
  await route.fulfill({
1884
- status: response.status || mock.status || 200,
1985
+ status,
1885
1986
  headers,
1886
1987
  contentType,
1887
1988
  body,
package/dist/cli.cjs CHANGED
@@ -7126,6 +7126,15 @@ function normalizeNetworkMock(input, index) {
7126
7126
  const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
7127
7127
  const responsesInput = input.responses ?? input.sequence;
7128
7128
  const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
7129
+ const requestBodyContains = normalizeStringList(
7130
+ input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
7131
+ `target.network_mocks[${index}].request_body_contains`
7132
+ );
7133
+ const requestBodyPatterns = normalizeStringList(
7134
+ input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
7135
+ `target.network_mocks[${index}].request_body_patterns`
7136
+ );
7137
+ validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
7129
7138
  const requiredHitCount = numberValue(
7130
7139
  input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
7131
7140
  );
@@ -7140,7 +7149,10 @@ function normalizeNetworkMock(input, index) {
7140
7149
  responses,
7141
7150
  repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
7142
7151
  required_hit_count: requiredHitCount,
7143
- required: input.required === false ? false : true
7152
+ required: input.required === false ? false : true,
7153
+ capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length),
7154
+ request_body_contains: requestBodyContains,
7155
+ request_body_patterns: requestBodyPatterns
7144
7156
  };
7145
7157
  }
7146
7158
  function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
@@ -7223,6 +7235,16 @@ function normalizeStringList(value, label) {
7223
7235
  if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
7224
7236
  return values;
7225
7237
  }
7238
+ function validateRegexPatterns(patterns, label) {
7239
+ for (const pattern of patterns || []) {
7240
+ try {
7241
+ new RegExp(pattern);
7242
+ } catch (error) {
7243
+ const message = error instanceof Error ? error.message : String(error);
7244
+ throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
7245
+ }
7246
+ }
7247
+ }
7226
7248
  function normalizeCheck(input, index) {
7227
7249
  if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
7228
7250
  const type = stringValue2(input.type);
@@ -7893,6 +7915,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
7893
7915
  reason: event.reason ?? event.error ?? "mock_failed"
7894
7916
  });
7895
7917
  }
7918
+ if (event.request_body_matches === false) {
7919
+ failed.push({
7920
+ label: event.label ?? null,
7921
+ url: event.url ?? null,
7922
+ method: event.method ?? null,
7923
+ reason: "request_body_mismatch",
7924
+ request_body_failures: event.request_body_failures ?? [],
7925
+ request_body_length: event.request_body_length ?? null,
7926
+ request_body_sample: event.request_body_sample ?? null
7927
+ });
7928
+ }
7896
7929
  }
7897
7930
  return {
7898
7931
  type: "network_mocks_succeeded",
@@ -8249,6 +8282,17 @@ function assessProfile(profile, evidence) {
8249
8282
  reason: event.reason || event.error || "mock_failed",
8250
8283
  });
8251
8284
  }
8285
+ if (event && event.request_body_matches === false) {
8286
+ failed.push({
8287
+ label: event.label || null,
8288
+ url: event.url || null,
8289
+ method: event.method || null,
8290
+ reason: "request_body_mismatch",
8291
+ request_body_failures: event.request_body_failures || [],
8292
+ request_body_length: event.request_body_length || null,
8293
+ request_body_sample: event.request_body_sample || null,
8294
+ });
8295
+ }
8252
8296
  }
8253
8297
  const hitsByLabel = {};
8254
8298
  const requiredHitsByLabel = {};
@@ -8697,6 +8741,52 @@ function compactSetupResultText(value) {
8697
8741
  if (text.length <= 500) return text;
8698
8742
  return text.slice(0, 500) + "... (" + text.length + " chars)";
8699
8743
  }
8744
+ function compactNetworkMockRequestBody(value) {
8745
+ const text = String(value || "").replace(/\s+/g, " ").trim();
8746
+ if (text.length <= 1000) return text;
8747
+ return text.slice(0, 1000) + "... (" + text.length + " chars)";
8748
+ }
8749
+ function networkMockStringList(value) {
8750
+ return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
8751
+ }
8752
+ function networkMockShouldCaptureRequestBody(mock) {
8753
+ return mock && (
8754
+ mock.capture_request_body === true
8755
+ || networkMockStringList(mock.request_body_contains).length > 0
8756
+ || networkMockStringList(mock.request_body_patterns).length > 0
8757
+ );
8758
+ }
8759
+ function networkMockRequestBodyFailures(body, mock) {
8760
+ const failures = [];
8761
+ const rawBody = String(body || "");
8762
+ const compactBody = compactNetworkMockRequestBody(rawBody);
8763
+ for (const expected of networkMockStringList(mock.request_body_contains)) {
8764
+ if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
8765
+ failures.push({
8766
+ type: "request_body_missing_text",
8767
+ text: String(expected).slice(0, 200),
8768
+ });
8769
+ }
8770
+ }
8771
+ for (const pattern of networkMockStringList(mock.request_body_patterns)) {
8772
+ try {
8773
+ const regex = new RegExp(pattern);
8774
+ if (!regex.test(rawBody) && !regex.test(compactBody)) {
8775
+ failures.push({
8776
+ type: "request_body_pattern_not_matched",
8777
+ pattern: String(pattern).slice(0, 200),
8778
+ });
8779
+ }
8780
+ } catch (error) {
8781
+ failures.push({
8782
+ type: "request_body_invalid_pattern",
8783
+ pattern: String(pattern).slice(0, 200),
8784
+ error: String(error && error.message ? error.message : error).slice(0, 500),
8785
+ });
8786
+ }
8787
+ }
8788
+ return failures;
8789
+ }
8700
8790
  async function setupLocatorVisible(locator, index) {
8701
8791
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
8702
8792
  }
@@ -8725,7 +8815,11 @@ async function registerNetworkMocks(mocks) {
8725
8815
  body = JSON.stringify(response.body_json);
8726
8816
  contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
8727
8817
  }
8728
- networkMockEvents.push({
8818
+ const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
8819
+ const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
8820
+ const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
8821
+ const status = response.status || mock.status || 200;
8822
+ const event = {
8729
8823
  ok: true,
8730
8824
  label: mock.label,
8731
8825
  response_label: response.label || null,
@@ -8735,10 +8829,17 @@ async function registerNetworkMocks(mocks) {
8735
8829
  sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
8736
8830
  url: request.url(),
8737
8831
  method,
8738
- status: response.status || mock.status || 200,
8739
- });
8832
+ status,
8833
+ };
8834
+ if (shouldCaptureRequestBody) {
8835
+ event.request_body_matches = requestBodyFailures.length === 0;
8836
+ event.request_body_failures = requestBodyFailures;
8837
+ event.request_body_length = requestBody.length;
8838
+ event.request_body_sample = compactNetworkMockRequestBody(requestBody);
8839
+ }
8840
+ networkMockEvents.push(event);
8740
8841
  await route.fulfill({
8741
- status: response.status || mock.status || 200,
8842
+ status,
8742
8843
  headers,
8743
8844
  contentType,
8744
8845
  body,
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-G673RKAC.js";
13
+ } from "./chunk-IJGMTTVP.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -8967,6 +8967,15 @@ function normalizeNetworkMock(input, index) {
8967
8967
  const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
8968
8968
  const responsesInput = input.responses ?? input.sequence;
8969
8969
  const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
8970
+ const requestBodyContains = normalizeStringList(
8971
+ input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
8972
+ `target.network_mocks[${index}].request_body_contains`
8973
+ );
8974
+ const requestBodyPatterns = normalizeStringList(
8975
+ input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
8976
+ `target.network_mocks[${index}].request_body_patterns`
8977
+ );
8978
+ validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
8970
8979
  const requiredHitCount = numberValue3(
8971
8980
  input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
8972
8981
  );
@@ -8981,7 +8990,10 @@ function normalizeNetworkMock(input, index) {
8981
8990
  responses,
8982
8991
  repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
8983
8992
  required_hit_count: requiredHitCount,
8984
- required: input.required === false ? false : true
8993
+ required: input.required === false ? false : true,
8994
+ capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length),
8995
+ request_body_contains: requestBodyContains,
8996
+ request_body_patterns: requestBodyPatterns
8985
8997
  };
8986
8998
  }
8987
8999
  function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
@@ -9064,6 +9076,16 @@ function normalizeStringList(value, label) {
9064
9076
  if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
9065
9077
  return values;
9066
9078
  }
9079
+ function validateRegexPatterns(patterns, label) {
9080
+ for (const pattern of patterns || []) {
9081
+ try {
9082
+ new RegExp(pattern);
9083
+ } catch (error) {
9084
+ const message = error instanceof Error ? error.message : String(error);
9085
+ throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
9086
+ }
9087
+ }
9088
+ }
9067
9089
  function normalizeCheck(input, index) {
9068
9090
  if (!isRecord2(input)) throw new Error(`checks[${index}] must be an object.`);
9069
9091
  const type = stringValue5(input.type);
@@ -9734,6 +9756,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
9734
9756
  reason: event.reason ?? event.error ?? "mock_failed"
9735
9757
  });
9736
9758
  }
9759
+ if (event.request_body_matches === false) {
9760
+ failed.push({
9761
+ label: event.label ?? null,
9762
+ url: event.url ?? null,
9763
+ method: event.method ?? null,
9764
+ reason: "request_body_mismatch",
9765
+ request_body_failures: event.request_body_failures ?? [],
9766
+ request_body_length: event.request_body_length ?? null,
9767
+ request_body_sample: event.request_body_sample ?? null
9768
+ });
9769
+ }
9737
9770
  }
9738
9771
  return {
9739
9772
  type: "network_mocks_succeeded",
@@ -10106,6 +10139,17 @@ function assessProfile(profile, evidence) {
10106
10139
  reason: event.reason || event.error || "mock_failed",
10107
10140
  });
10108
10141
  }
10142
+ if (event && event.request_body_matches === false) {
10143
+ failed.push({
10144
+ label: event.label || null,
10145
+ url: event.url || null,
10146
+ method: event.method || null,
10147
+ reason: "request_body_mismatch",
10148
+ request_body_failures: event.request_body_failures || [],
10149
+ request_body_length: event.request_body_length || null,
10150
+ request_body_sample: event.request_body_sample || null,
10151
+ });
10152
+ }
10109
10153
  }
10110
10154
  const hitsByLabel = {};
10111
10155
  const requiredHitsByLabel = {};
@@ -10554,6 +10598,52 @@ function compactSetupResultText(value) {
10554
10598
  if (text.length <= 500) return text;
10555
10599
  return text.slice(0, 500) + "... (" + text.length + " chars)";
10556
10600
  }
10601
+ function compactNetworkMockRequestBody(value) {
10602
+ const text = String(value || "").replace(/\s+/g, " ").trim();
10603
+ if (text.length <= 1000) return text;
10604
+ return text.slice(0, 1000) + "... (" + text.length + " chars)";
10605
+ }
10606
+ function networkMockStringList(value) {
10607
+ return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
10608
+ }
10609
+ function networkMockShouldCaptureRequestBody(mock) {
10610
+ return mock && (
10611
+ mock.capture_request_body === true
10612
+ || networkMockStringList(mock.request_body_contains).length > 0
10613
+ || networkMockStringList(mock.request_body_patterns).length > 0
10614
+ );
10615
+ }
10616
+ function networkMockRequestBodyFailures(body, mock) {
10617
+ const failures = [];
10618
+ const rawBody = String(body || "");
10619
+ const compactBody = compactNetworkMockRequestBody(rawBody);
10620
+ for (const expected of networkMockStringList(mock.request_body_contains)) {
10621
+ if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
10622
+ failures.push({
10623
+ type: "request_body_missing_text",
10624
+ text: String(expected).slice(0, 200),
10625
+ });
10626
+ }
10627
+ }
10628
+ for (const pattern of networkMockStringList(mock.request_body_patterns)) {
10629
+ try {
10630
+ const regex = new RegExp(pattern);
10631
+ if (!regex.test(rawBody) && !regex.test(compactBody)) {
10632
+ failures.push({
10633
+ type: "request_body_pattern_not_matched",
10634
+ pattern: String(pattern).slice(0, 200),
10635
+ });
10636
+ }
10637
+ } catch (error) {
10638
+ failures.push({
10639
+ type: "request_body_invalid_pattern",
10640
+ pattern: String(pattern).slice(0, 200),
10641
+ error: String(error && error.message ? error.message : error).slice(0, 500),
10642
+ });
10643
+ }
10644
+ }
10645
+ return failures;
10646
+ }
10557
10647
  async function setupLocatorVisible(locator, index) {
10558
10648
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
10559
10649
  }
@@ -10582,7 +10672,11 @@ async function registerNetworkMocks(mocks) {
10582
10672
  body = JSON.stringify(response.body_json);
10583
10673
  contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
10584
10674
  }
10585
- networkMockEvents.push({
10675
+ const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
10676
+ const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
10677
+ const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
10678
+ const status = response.status || mock.status || 200;
10679
+ const event = {
10586
10680
  ok: true,
10587
10681
  label: mock.label,
10588
10682
  response_label: response.label || null,
@@ -10592,10 +10686,17 @@ async function registerNetworkMocks(mocks) {
10592
10686
  sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
10593
10687
  url: request.url(),
10594
10688
  method,
10595
- status: response.status || mock.status || 200,
10596
- });
10689
+ status,
10690
+ };
10691
+ if (shouldCaptureRequestBody) {
10692
+ event.request_body_matches = requestBodyFailures.length === 0;
10693
+ event.request_body_failures = requestBodyFailures;
10694
+ event.request_body_length = requestBody.length;
10695
+ event.request_body_sample = compactNetworkMockRequestBody(requestBody);
10696
+ }
10697
+ networkMockEvents.push(event);
10597
10698
  await route.fulfill({
10598
- status: response.status || mock.status || 200,
10699
+ status,
10599
10700
  headers,
10600
10701
  contentType,
10601
10702
  body,
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-G673RKAC.js";
61
+ } from "./chunk-IJGMTTVP.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -296,6 +296,15 @@ function normalizeNetworkMock(input, index) {
296
296
  const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
297
297
  const responsesInput = input.responses ?? input.sequence;
298
298
  const responses = normalizeNetworkMockResponses(responsesInput, index, payload);
299
+ const requestBodyContains = normalizeStringList(
300
+ input.request_body_contains ?? input.requestBodyContains ?? input.body_contains ?? input.bodyContains,
301
+ `target.network_mocks[${index}].request_body_contains`
302
+ );
303
+ const requestBodyPatterns = normalizeStringList(
304
+ input.request_body_patterns ?? input.requestBodyPatterns ?? input.body_patterns ?? input.bodyPatterns,
305
+ `target.network_mocks[${index}].request_body_patterns`
306
+ );
307
+ validateRegexPatterns(requestBodyPatterns, `target.network_mocks[${index}].request_body_patterns`);
299
308
  const requiredHitCount = numberValue(
300
309
  input.required_hit_count ?? input.requiredHitCount ?? input.required_hits ?? input.requiredHits ?? input.min_hits ?? input.minHits
301
310
  );
@@ -310,7 +319,10 @@ function normalizeNetworkMock(input, index) {
310
319
  responses,
311
320
  repeat_responses: input.repeat_responses === true || input.repeatResponses === true || input.cycle_responses === true || input.cycleResponses === true,
312
321
  required_hit_count: requiredHitCount,
313
- required: input.required === false ? false : true
322
+ required: input.required === false ? false : true,
323
+ capture_request_body: input.capture_request_body === true || input.captureRequestBody === true || Boolean(requestBodyContains?.length) || Boolean(requestBodyPatterns?.length),
324
+ request_body_contains: requestBodyContains,
325
+ request_body_patterns: requestBodyPatterns
314
326
  };
315
327
  }
316
328
  function normalizeNetworkMockResponsePayload(input, label, defaults = {}) {
@@ -393,6 +405,16 @@ function normalizeStringList(value, label) {
393
405
  if (!values.length) throw new Error(`${label} must contain non-empty strings.`);
394
406
  return values;
395
407
  }
408
+ function validateRegexPatterns(patterns, label) {
409
+ for (const pattern of patterns || []) {
410
+ try {
411
+ new RegExp(pattern);
412
+ } catch (error) {
413
+ const message = error instanceof Error ? error.message : String(error);
414
+ throw new Error(`${label} contains invalid regex ${JSON.stringify(pattern)}: ${message}`);
415
+ }
416
+ }
417
+ }
396
418
  function normalizeCheck(input, index) {
397
419
  if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
398
420
  const type = stringValue(input.type);
@@ -1063,6 +1085,17 @@ function assessNetworkMocksFromEvidence(profile, evidence) {
1063
1085
  reason: event.reason ?? event.error ?? "mock_failed"
1064
1086
  });
1065
1087
  }
1088
+ if (event.request_body_matches === false) {
1089
+ failed.push({
1090
+ label: event.label ?? null,
1091
+ url: event.url ?? null,
1092
+ method: event.method ?? null,
1093
+ reason: "request_body_mismatch",
1094
+ request_body_failures: event.request_body_failures ?? [],
1095
+ request_body_length: event.request_body_length ?? null,
1096
+ request_body_sample: event.request_body_sample ?? null
1097
+ });
1098
+ }
1066
1099
  }
1067
1100
  return {
1068
1101
  type: "network_mocks_succeeded",
@@ -1435,6 +1468,17 @@ function assessProfile(profile, evidence) {
1435
1468
  reason: event.reason || event.error || "mock_failed",
1436
1469
  });
1437
1470
  }
1471
+ if (event && event.request_body_matches === false) {
1472
+ failed.push({
1473
+ label: event.label || null,
1474
+ url: event.url || null,
1475
+ method: event.method || null,
1476
+ reason: "request_body_mismatch",
1477
+ request_body_failures: event.request_body_failures || [],
1478
+ request_body_length: event.request_body_length || null,
1479
+ request_body_sample: event.request_body_sample || null,
1480
+ });
1481
+ }
1438
1482
  }
1439
1483
  const hitsByLabel = {};
1440
1484
  const requiredHitsByLabel = {};
@@ -1883,6 +1927,52 @@ function compactSetupResultText(value) {
1883
1927
  if (text.length <= 500) return text;
1884
1928
  return text.slice(0, 500) + "... (" + text.length + " chars)";
1885
1929
  }
1930
+ function compactNetworkMockRequestBody(value) {
1931
+ const text = String(value || "").replace(/\s+/g, " ").trim();
1932
+ if (text.length <= 1000) return text;
1933
+ return text.slice(0, 1000) + "... (" + text.length + " chars)";
1934
+ }
1935
+ function networkMockStringList(value) {
1936
+ return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
1937
+ }
1938
+ function networkMockShouldCaptureRequestBody(mock) {
1939
+ return mock && (
1940
+ mock.capture_request_body === true
1941
+ || networkMockStringList(mock.request_body_contains).length > 0
1942
+ || networkMockStringList(mock.request_body_patterns).length > 0
1943
+ );
1944
+ }
1945
+ function networkMockRequestBodyFailures(body, mock) {
1946
+ const failures = [];
1947
+ const rawBody = String(body || "");
1948
+ const compactBody = compactNetworkMockRequestBody(rawBody);
1949
+ for (const expected of networkMockStringList(mock.request_body_contains)) {
1950
+ if (!rawBody.includes(expected) && !compactBody.includes(expected)) {
1951
+ failures.push({
1952
+ type: "request_body_missing_text",
1953
+ text: String(expected).slice(0, 200),
1954
+ });
1955
+ }
1956
+ }
1957
+ for (const pattern of networkMockStringList(mock.request_body_patterns)) {
1958
+ try {
1959
+ const regex = new RegExp(pattern);
1960
+ if (!regex.test(rawBody) && !regex.test(compactBody)) {
1961
+ failures.push({
1962
+ type: "request_body_pattern_not_matched",
1963
+ pattern: String(pattern).slice(0, 200),
1964
+ });
1965
+ }
1966
+ } catch (error) {
1967
+ failures.push({
1968
+ type: "request_body_invalid_pattern",
1969
+ pattern: String(pattern).slice(0, 200),
1970
+ error: String(error && error.message ? error.message : error).slice(0, 500),
1971
+ });
1972
+ }
1973
+ }
1974
+ return failures;
1975
+ }
1886
1976
  async function setupLocatorVisible(locator, index) {
1887
1977
  return await locator.nth(index).isVisible({ timeout: 1000 }).catch(() => false);
1888
1978
  }
@@ -1911,7 +2001,11 @@ async function registerNetworkMocks(mocks) {
1911
2001
  body = JSON.stringify(response.body_json);
1912
2002
  contentType = response.content_type || headers["content-type"] || headers["Content-Type"] || "application/json";
1913
2003
  }
1914
- networkMockEvents.push({
2004
+ const shouldCaptureRequestBody = networkMockShouldCaptureRequestBody(mock);
2005
+ const requestBody = shouldCaptureRequestBody && request.postData ? request.postData() || "" : "";
2006
+ const requestBodyFailures = shouldCaptureRequestBody ? networkMockRequestBodyFailures(requestBody, mock) : [];
2007
+ const status = response.status || mock.status || 200;
2008
+ const event = {
1915
2009
  ok: true,
1916
2010
  label: mock.label,
1917
2011
  response_label: response.label || null,
@@ -1921,10 +2015,17 @@ async function registerNetworkMocks(mocks) {
1921
2015
  sequence_cycle: responseIndex !== null && mock.repeat_responses === true && hitIndex >= responses.length,
1922
2016
  url: request.url(),
1923
2017
  method,
1924
- status: response.status || mock.status || 200,
1925
- });
2018
+ status,
2019
+ };
2020
+ if (shouldCaptureRequestBody) {
2021
+ event.request_body_matches = requestBodyFailures.length === 0;
2022
+ event.request_body_failures = requestBodyFailures;
2023
+ event.request_body_length = requestBody.length;
2024
+ event.request_body_sample = compactNetworkMockRequestBody(requestBody);
2025
+ }
2026
+ networkMockEvents.push(event);
1926
2027
  await route.fulfill({
1927
- status: response.status || mock.status || 200,
2028
+ status,
1928
2029
  headers,
1929
2030
  contentType,
1930
2031
  body,
@@ -50,6 +50,9 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
50
50
  repeat_responses?: boolean;
51
51
  required_hit_count?: number;
52
52
  required?: boolean;
53
+ capture_request_body?: boolean;
54
+ request_body_contains?: string[];
55
+ request_body_patterns?: string[];
53
56
  }
54
57
  interface RiddleProofProfileRouteInventoryRoute {
55
58
  name?: string;
package/dist/profile.d.ts CHANGED
@@ -50,6 +50,9 @@ interface RiddleProofProfileNetworkMock extends RiddleProofProfileNetworkMockRes
50
50
  repeat_responses?: boolean;
51
51
  required_hit_count?: number;
52
52
  required?: boolean;
53
+ capture_request_body?: boolean;
54
+ request_body_contains?: string[];
55
+ request_body_patterns?: string[];
53
56
  }
54
57
  interface RiddleProofProfileRouteInventoryRoute {
55
58
  name?: string;
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-G673RKAC.js";
22
+ } from "./chunk-IJGMTTVP.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.44",
3
+ "version": "0.7.45",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",