@rogatio/cli 6.9.0 → 6.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor/index.css +35 -1
- package/dist/editor/index.js +388 -160
- package/dist/node/index.js +95 -70
- package/package.json +1 -1
package/dist/editor/index.css
CHANGED
|
@@ -729,6 +729,10 @@
|
|
|
729
729
|
.rogatio-editor :focus-visible {
|
|
730
730
|
outline-color: Highlight;
|
|
731
731
|
}
|
|
732
|
+
.rogatio-editor [data-test-rule-link] {
|
|
733
|
+
color: LinkText;
|
|
734
|
+
background: transparent;
|
|
735
|
+
}
|
|
732
736
|
.rogatio-editor [data-desktop-route-rail] {
|
|
733
737
|
border-bottom-color: ButtonText;
|
|
734
738
|
}
|
|
@@ -741,10 +745,40 @@
|
|
|
741
745
|
transition: none;
|
|
742
746
|
}
|
|
743
747
|
}
|
|
744
|
-
.rogatio-editor [data-test-description]
|
|
748
|
+
.rogatio-editor [data-test-description],
|
|
749
|
+
.rogatio-editor [data-test-checking] {
|
|
745
750
|
color: var(--rogatio-muted);
|
|
746
751
|
margin-block: 0 1rem;
|
|
747
752
|
}
|
|
753
|
+
.rogatio-editor [data-test-samples] {
|
|
754
|
+
display: flex;
|
|
755
|
+
flex-wrap: wrap;
|
|
756
|
+
gap: 0.5rem;
|
|
757
|
+
margin-top: 0.75rem;
|
|
758
|
+
}
|
|
759
|
+
.rogatio-editor [data-test-outcome] {
|
|
760
|
+
margin: 0;
|
|
761
|
+
}
|
|
762
|
+
.rogatio-editor [data-test-rule-link] {
|
|
763
|
+
display: inline;
|
|
764
|
+
margin: 0;
|
|
765
|
+
padding: 0;
|
|
766
|
+
border: 0;
|
|
767
|
+
background: none;
|
|
768
|
+
color: var(--rogatio-primary);
|
|
769
|
+
font: inherit;
|
|
770
|
+
text-decoration: underline;
|
|
771
|
+
cursor: pointer;
|
|
772
|
+
}
|
|
773
|
+
.rogatio-editor [data-test-misses] {
|
|
774
|
+
margin-top: 0.5rem;
|
|
775
|
+
}
|
|
776
|
+
.rogatio-editor [data-test-misses] summary {
|
|
777
|
+
cursor: pointer;
|
|
778
|
+
}
|
|
779
|
+
.rogatio-editor [data-test-miss-reason] {
|
|
780
|
+
color: var(--rogatio-muted);
|
|
781
|
+
}
|
|
748
782
|
.rogatio-editor [data-test-panel] {
|
|
749
783
|
display: grid;
|
|
750
784
|
gap: 1rem;
|
package/dist/editor/index.js
CHANGED
|
@@ -1742,6 +1742,31 @@ var HTTP_METHODS2 = [
|
|
|
1742
1742
|
"CONNECT",
|
|
1743
1743
|
"TRACE"
|
|
1744
1744
|
];
|
|
1745
|
+
var RESOURCE_TYPE_LABELS = {
|
|
1746
|
+
main_frame: "Page",
|
|
1747
|
+
sub_frame: "Frame",
|
|
1748
|
+
stylesheet: "Stylesheet",
|
|
1749
|
+
script: "Script",
|
|
1750
|
+
image: "Image",
|
|
1751
|
+
font: "Font",
|
|
1752
|
+
object: "Plugin",
|
|
1753
|
+
media: "Media",
|
|
1754
|
+
xmlhttprequest: "Fetch",
|
|
1755
|
+
ping: "Ping",
|
|
1756
|
+
csp_report: "CSP report",
|
|
1757
|
+
websocket: "WebSocket",
|
|
1758
|
+
webtransport: "WebTransport",
|
|
1759
|
+
webbundle: "Web Bundle",
|
|
1760
|
+
other: "Other"
|
|
1761
|
+
};
|
|
1762
|
+
var TEST_CASE_LIMIT = 256;
|
|
1763
|
+
var RULE_TYPE_LABELS = {
|
|
1764
|
+
redirect: "Redirect",
|
|
1765
|
+
query: "Query parameters",
|
|
1766
|
+
header: "Header",
|
|
1767
|
+
"response-body": "Response body",
|
|
1768
|
+
"request-body": "Request body"
|
|
1769
|
+
};
|
|
1745
1770
|
var COMMON_RULE_FIELDS = /* @__PURE__ */ new Set([
|
|
1746
1771
|
"id",
|
|
1747
1772
|
"name",
|
|
@@ -2033,6 +2058,80 @@ function displayName(value, fallback) {
|
|
|
2033
2058
|
const text = safeText(value, fallback);
|
|
2034
2059
|
return text.length > 0 ? text : fallback;
|
|
2035
2060
|
}
|
|
2061
|
+
function checkingCopy(method, resourceType) {
|
|
2062
|
+
if (method === "GET" && resourceType === "main_frame") {
|
|
2063
|
+
return "Checking these as page loads (GET).";
|
|
2064
|
+
}
|
|
2065
|
+
if (method === "" && resourceType === "") {
|
|
2066
|
+
return "The method and the resource type are not tested.";
|
|
2067
|
+
}
|
|
2068
|
+
const resourceLabel = resourceType === "" ? "" : RESOURCE_TYPE_LABELS[resourceType];
|
|
2069
|
+
if (method === "") {
|
|
2070
|
+
const subject = resourceType === "main_frame" ? "page loads" : `${resourceLabel} requests`;
|
|
2071
|
+
return `Checking these as ${subject}. The method is not tested.`;
|
|
2072
|
+
}
|
|
2073
|
+
if (resourceType === "") {
|
|
2074
|
+
return `Checking these as ${method} requests. The resource type is not tested.`;
|
|
2075
|
+
}
|
|
2076
|
+
if (resourceType === "main_frame") {
|
|
2077
|
+
return `Checking these as page loads (${method}).`;
|
|
2078
|
+
}
|
|
2079
|
+
return `Checking these as ${resourceLabel} requests (${method}).`;
|
|
2080
|
+
}
|
|
2081
|
+
function exactUrlFromSource(source) {
|
|
2082
|
+
if (source?.key !== "url" || source.operator !== "regex") {
|
|
2083
|
+
return null;
|
|
2084
|
+
}
|
|
2085
|
+
if (typeof source.value !== "string") return null;
|
|
2086
|
+
const value = source.value;
|
|
2087
|
+
if (!value.startsWith("^") || !value.endsWith("$") || value.length < 2) {
|
|
2088
|
+
return null;
|
|
2089
|
+
}
|
|
2090
|
+
let decoded = "";
|
|
2091
|
+
for (let index = 1; index < value.length - 1; index += 1) {
|
|
2092
|
+
const char = value[index];
|
|
2093
|
+
if (char === "\\") {
|
|
2094
|
+
index += 1;
|
|
2095
|
+
const escaped = value[index];
|
|
2096
|
+
if (escaped === void 0 || index >= value.length - 1) return null;
|
|
2097
|
+
decoded += escaped;
|
|
2098
|
+
continue;
|
|
2099
|
+
}
|
|
2100
|
+
if ("^$\\.*+?()[]{}|".includes(char)) return null;
|
|
2101
|
+
decoded += char;
|
|
2102
|
+
}
|
|
2103
|
+
const converted = urlToExactRegex(decoded);
|
|
2104
|
+
if (!converted.ok || converted.source !== value) return null;
|
|
2105
|
+
return decoded;
|
|
2106
|
+
}
|
|
2107
|
+
function isDryRunResult(value) {
|
|
2108
|
+
if (!isRecord4(value)) return false;
|
|
2109
|
+
const summary = value.summary;
|
|
2110
|
+
return Array.isArray(value.results) && Array.isArray(value.errors) && isRecord4(summary) && typeof summary.matchedUrlCount === "number" && typeof summary.urlCount === "number";
|
|
2111
|
+
}
|
|
2112
|
+
function previewSentence(preview) {
|
|
2113
|
+
if (!preview || preview.summary.length === 0) return null;
|
|
2114
|
+
switch (preview.kind) {
|
|
2115
|
+
case "redirect":
|
|
2116
|
+
return `The browser would go to ${preview.summary}.`;
|
|
2117
|
+
case "query":
|
|
2118
|
+
return `The query would be ${preview.summary}.`;
|
|
2119
|
+
case "header":
|
|
2120
|
+
return `The header would be ${preview.summary}.`;
|
|
2121
|
+
case "request-body":
|
|
2122
|
+
return `The request body would be ${preview.summary}.`;
|
|
2123
|
+
case "response-body":
|
|
2124
|
+
return `The response body would be ${preview.summary}.`;
|
|
2125
|
+
default:
|
|
2126
|
+
return null;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
function missReason(rule) {
|
|
2130
|
+
if (rule.source.state === "unmatched") return "URL pattern";
|
|
2131
|
+
if (rule.method.state === "unmatched") return "method";
|
|
2132
|
+
if (rule.resourceType.state === "unmatched") return "resource type";
|
|
2133
|
+
return "URL pattern";
|
|
2134
|
+
}
|
|
2036
2135
|
function isHTMLElement(value) {
|
|
2037
2136
|
return value !== null && typeof value === "object" && value.nodeType === 1 && typeof value.appendChild === "function";
|
|
2038
2137
|
}
|
|
@@ -2171,9 +2270,8 @@ var EditorControllerImpl = class {
|
|
|
2171
2270
|
controlNumber = 0;
|
|
2172
2271
|
previousFocus;
|
|
2173
2272
|
testUrls = "";
|
|
2174
|
-
testMethod = "";
|
|
2175
|
-
testResourceType = "";
|
|
2176
|
-
testMaxCases = "256";
|
|
2273
|
+
testMethod = "GET";
|
|
2274
|
+
testResourceType = "main_frame";
|
|
2177
2275
|
testResult = void 0;
|
|
2178
2276
|
testRunning = false;
|
|
2179
2277
|
testRequestId = 0;
|
|
@@ -2349,10 +2447,6 @@ var EditorControllerImpl = class {
|
|
|
2349
2447
|
this.testUrls = target.value;
|
|
2350
2448
|
return;
|
|
2351
2449
|
}
|
|
2352
|
-
if (target.dataset.testMaxCases !== void 0) {
|
|
2353
|
-
this.testMaxCases = target.value;
|
|
2354
|
-
return;
|
|
2355
|
-
}
|
|
2356
2450
|
const path = target.dataset.path;
|
|
2357
2451
|
if (!path || target.type === "checkbox" || this.extensionControls.has(path))
|
|
2358
2452
|
return;
|
|
@@ -2387,10 +2481,12 @@ var EditorControllerImpl = class {
|
|
|
2387
2481
|
}
|
|
2388
2482
|
if (target instanceof HTMLSelectElement && target.dataset.testMethod !== void 0) {
|
|
2389
2483
|
this.testMethod = target.value || "";
|
|
2484
|
+
this.render();
|
|
2390
2485
|
return;
|
|
2391
2486
|
}
|
|
2392
2487
|
if (target instanceof HTMLSelectElement && target.dataset.testResourceType !== void 0) {
|
|
2393
2488
|
this.testResourceType = target.value || "";
|
|
2489
|
+
this.render();
|
|
2394
2490
|
return;
|
|
2395
2491
|
}
|
|
2396
2492
|
const path = target.dataset.path;
|
|
@@ -2571,6 +2667,17 @@ var EditorControllerImpl = class {
|
|
|
2571
2667
|
void this.runTest();
|
|
2572
2668
|
return;
|
|
2573
2669
|
}
|
|
2670
|
+
if (command === "test:open-rule") {
|
|
2671
|
+
this.navigateToRule(element.dataset.groupId, element.dataset.ruleId);
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
if (command === "test:try-url") {
|
|
2675
|
+
const url = element.dataset.url;
|
|
2676
|
+
if (!url) return;
|
|
2677
|
+
this.testUrls = url;
|
|
2678
|
+
this.statusMessage = "";
|
|
2679
|
+
this.render();
|
|
2680
|
+
}
|
|
2574
2681
|
}
|
|
2575
2682
|
collectDiagnostics(value) {
|
|
2576
2683
|
let hostDiagnostics;
|
|
@@ -3282,65 +3389,111 @@ var EditorControllerImpl = class {
|
|
|
3282
3389
|
this.focusRequest = path;
|
|
3283
3390
|
this.render();
|
|
3284
3391
|
}
|
|
3392
|
+
ruleCount() {
|
|
3393
|
+
let count = 0;
|
|
3394
|
+
for (const group of this.draft.groups) count += group.rules.length;
|
|
3395
|
+
return count;
|
|
3396
|
+
}
|
|
3397
|
+
testLines() {
|
|
3398
|
+
const entries = [];
|
|
3399
|
+
const rawLines = this.testUrls.split(/\r?\n/);
|
|
3400
|
+
for (let index = 0; index < rawLines.length; index += 1) {
|
|
3401
|
+
const url = rawLines[index]?.trim() ?? "";
|
|
3402
|
+
if (url.length > 0) entries.push({ url, line: index + 1 });
|
|
3403
|
+
}
|
|
3404
|
+
return entries;
|
|
3405
|
+
}
|
|
3406
|
+
showDryRunDiagnostics(diagnostics) {
|
|
3407
|
+
this.testResult = void 0;
|
|
3408
|
+
this.testRunning = false;
|
|
3409
|
+
this.errors = [...diagnostics];
|
|
3410
|
+
const count = diagnostics.length;
|
|
3411
|
+
this.statusMessage = count === 0 ? "The project could not be tested." : `${count} validation error${count === 1 ? "" : "s"} found.`;
|
|
3412
|
+
const first = diagnostics[0];
|
|
3413
|
+
if (first) {
|
|
3414
|
+
this.navigateToPath(first.path);
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
this.render();
|
|
3418
|
+
}
|
|
3285
3419
|
async runTest() {
|
|
3286
3420
|
if (!this.options.dryRun) {
|
|
3287
3421
|
this.statusMessage = "Dry-run is not configured for this editor instance.";
|
|
3288
3422
|
this.render();
|
|
3289
3423
|
return;
|
|
3290
3424
|
}
|
|
3291
|
-
|
|
3292
|
-
|
|
3425
|
+
if (this.ruleCount() === 0) {
|
|
3426
|
+
this.testResult = void 0;
|
|
3427
|
+
this.testRunning = false;
|
|
3428
|
+
this.statusMessage = "This project has no rules to test.";
|
|
3429
|
+
this.render();
|
|
3430
|
+
return;
|
|
3431
|
+
}
|
|
3432
|
+
const lines = this.testLines();
|
|
3433
|
+
if (lines.length === 0) {
|
|
3293
3434
|
this.statusMessage = "Please enter at least one URL to test.";
|
|
3294
3435
|
this.render();
|
|
3295
3436
|
return;
|
|
3296
3437
|
}
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
const
|
|
3438
|
+
if (lines.length > TEST_CASE_LIMIT) {
|
|
3439
|
+
this.testResult = void 0;
|
|
3440
|
+
this.testRunning = false;
|
|
3441
|
+
this.statusMessage = "Only 256 URLs can be checked at once.";
|
|
3442
|
+
this.render();
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
const cases = lines.map((entry) => {
|
|
3446
|
+
const testCase = { url: entry.url };
|
|
3447
|
+
if (this.testMethod !== "") testCase.method = this.testMethod;
|
|
3448
|
+
if (this.testResourceType !== "") {
|
|
3449
|
+
testCase.resourceType = this.testResourceType;
|
|
3450
|
+
}
|
|
3451
|
+
return testCase;
|
|
3452
|
+
});
|
|
3305
3453
|
const requestId = ++this.testRequestId;
|
|
3306
3454
|
this.testRunning = true;
|
|
3307
|
-
this.statusMessage = "
|
|
3455
|
+
this.statusMessage = "Checking URLs...";
|
|
3308
3456
|
this.render();
|
|
3309
3457
|
try {
|
|
3310
|
-
const
|
|
3311
|
-
this.getDraft(),
|
|
3312
|
-
cases,
|
|
3313
|
-
maxCases === void 0 ? void 0 : { maxCases }
|
|
3314
|
-
);
|
|
3458
|
+
const outcome = await this.options.dryRun(this.getDraft(), cases);
|
|
3315
3459
|
if (requestId !== this.testRequestId) return;
|
|
3316
|
-
this.testResult = result;
|
|
3317
3460
|
this.testRunning = false;
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3461
|
+
if (isDryRunResult(outcome)) {
|
|
3462
|
+
this.testResult = outcome;
|
|
3463
|
+
const checked = outcome.summary.urlCount;
|
|
3464
|
+
this.statusMessage = outcome.errors.length > 0 ? `Checked ${checked} URL${checked === 1 ? "" : "s"}. Some lines are not http(s) URLs.` : `Checked ${checked} URL${checked === 1 ? "" : "s"}.`;
|
|
3465
|
+
this.render();
|
|
3466
|
+
return;
|
|
3467
|
+
}
|
|
3468
|
+
const diagnostics = normalizeDiagnostics(
|
|
3469
|
+
isRecord4(outcome) ? outcome.diagnostics : void 0
|
|
3470
|
+
);
|
|
3471
|
+
this.showDryRunDiagnostics(diagnostics);
|
|
3321
3472
|
} catch (e) {
|
|
3322
3473
|
if (requestId !== this.testRequestId) return;
|
|
3474
|
+
this.testResult = void 0;
|
|
3323
3475
|
this.testRunning = false;
|
|
3324
3476
|
const message = e instanceof Error ? e.message : "Test failed";
|
|
3325
3477
|
this.statusMessage = `Test error: ${message}`;
|
|
3478
|
+
this.render();
|
|
3326
3479
|
}
|
|
3327
|
-
this.render();
|
|
3328
3480
|
}
|
|
3329
3481
|
renderTestResults(result) {
|
|
3330
3482
|
const resultsSection = this.host.querySelector(
|
|
3331
3483
|
"[data-test-results]"
|
|
3332
3484
|
);
|
|
3333
|
-
if (!resultsSection) return;
|
|
3485
|
+
if (!resultsSection || !isDryRunResult(result)) return;
|
|
3334
3486
|
resultsSection.replaceChildren();
|
|
3335
3487
|
const heading = this.document.createElement("h3");
|
|
3336
3488
|
heading.textContent = "Results";
|
|
3337
3489
|
resultsSection.append(heading);
|
|
3490
|
+
const lines = this.testLines();
|
|
3338
3491
|
if (result.errors.length > 0) {
|
|
3339
3492
|
const errorsList = this.document.createElement("ul");
|
|
3340
3493
|
errorsList.dataset.testErrors = "true";
|
|
3341
3494
|
for (const err of result.errors) {
|
|
3342
3495
|
const item = this.document.createElement("li");
|
|
3343
|
-
item.textContent =
|
|
3496
|
+
item.textContent = this.testErrorText(err, lines);
|
|
3344
3497
|
errorsList.append(item);
|
|
3345
3498
|
}
|
|
3346
3499
|
resultsSection.append(errorsList);
|
|
@@ -3355,73 +3508,135 @@ var EditorControllerImpl = class {
|
|
|
3355
3508
|
for (const urlResult of result.results) {
|
|
3356
3509
|
const card = this.document.createElement("article");
|
|
3357
3510
|
card.dataset.testResultCard = "true";
|
|
3358
|
-
const
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
urlBadge.dataset.variant = matched ? "matched" : "unmatched";
|
|
3367
|
-
urlBadge.textContent = matched ? "MATCHED" : "NO MATCH";
|
|
3368
|
-
urlHeader.append(urlText, urlBadge);
|
|
3369
|
-
card.append(urlHeader);
|
|
3370
|
-
for (const rule of urlResult.rules) {
|
|
3371
|
-
const ruleDiv = this.document.createElement("div");
|
|
3372
|
-
ruleDiv.dataset.testRule = "true";
|
|
3373
|
-
const ruleHeader = this.document.createElement("div");
|
|
3374
|
-
ruleHeader.dataset.testRuleHeader = "true";
|
|
3375
|
-
const ruleName = this.document.createElement("strong");
|
|
3376
|
-
ruleName.textContent = this.testRuleLabel(rule.groupId, rule.ruleId);
|
|
3377
|
-
const ruleBadge = this.document.createElement("span");
|
|
3378
|
-
ruleBadge.dataset.testBadge = "true";
|
|
3379
|
-
ruleBadge.dataset.variant = rule.matched ? "matched" : "unmatched";
|
|
3380
|
-
ruleBadge.textContent = rule.matched ? "\u2713 MATCHED" : "\u2717 NOT MATCHED";
|
|
3381
|
-
ruleHeader.append(ruleName, ruleBadge);
|
|
3382
|
-
ruleDiv.append(ruleHeader);
|
|
3383
|
-
const dims = [
|
|
3384
|
-
{ label: "source", dim: rule.source },
|
|
3385
|
-
{ label: "method", dim: rule.method },
|
|
3386
|
-
{ label: "resourceType", dim: rule.resourceType }
|
|
3387
|
-
];
|
|
3388
|
-
for (const { label, dim } of dims) {
|
|
3389
|
-
const dimDiv = this.document.createElement("div");
|
|
3390
|
-
dimDiv.dataset.testDimension = "true";
|
|
3391
|
-
const badge = this.document.createElement("span");
|
|
3392
|
-
badge.dataset.testBadge = "true";
|
|
3393
|
-
badge.dataset.variant = dim.state === "matched" ? "matched" : dim.state === "unmatched" ? "unmatched" : "na";
|
|
3394
|
-
badge.textContent = dim.state.toUpperCase();
|
|
3395
|
-
dimDiv.append(
|
|
3396
|
-
badge,
|
|
3397
|
-
this.document.createTextNode(`${label}: ${dim.detail}`)
|
|
3398
|
-
);
|
|
3399
|
-
ruleDiv.append(dimDiv);
|
|
3400
|
-
}
|
|
3401
|
-
if (rule.actionPreview) {
|
|
3402
|
-
const apDiv = this.document.createElement("div");
|
|
3403
|
-
apDiv.dataset.testActionPreview = "true";
|
|
3404
|
-
apDiv.textContent = `Action preview: ${rule.actionPreview.kind} - ${rule.actionPreview.summary}`;
|
|
3405
|
-
ruleDiv.append(apDiv);
|
|
3406
|
-
}
|
|
3407
|
-
card.append(ruleDiv);
|
|
3511
|
+
const matches = urlResult.rules.filter((rule) => rule.matched);
|
|
3512
|
+
const misses = urlResult.rules.filter((rule) => !rule.matched);
|
|
3513
|
+
card.dataset.matched = matches.length > 0 ? "true" : "false";
|
|
3514
|
+
for (const rule of matches) {
|
|
3515
|
+
card.append(this.renderTestOutcome(urlResult.url, rule));
|
|
3516
|
+
}
|
|
3517
|
+
if (misses.length > 0) {
|
|
3518
|
+
card.append(this.renderTestMisses(misses));
|
|
3408
3519
|
}
|
|
3409
3520
|
resultsSection.append(card);
|
|
3410
3521
|
}
|
|
3411
3522
|
}
|
|
3523
|
+
testErrorText(err, lines) {
|
|
3524
|
+
if (err.code === "dryrun.batch-limit") {
|
|
3525
|
+
return "Only 256 URLs can be checked at once.";
|
|
3526
|
+
}
|
|
3527
|
+
const entry = err.index === void 0 ? void 0 : lines[err.index];
|
|
3528
|
+
if (err.code === "dryrun.invalid-url" && entry) {
|
|
3529
|
+
return `Line ${entry.line} is not an http(s) URL: ${entry.url}`;
|
|
3530
|
+
}
|
|
3531
|
+
if (entry) {
|
|
3532
|
+
return `Line ${entry.line} could not be checked: ${entry.url}`;
|
|
3533
|
+
}
|
|
3534
|
+
return err.message;
|
|
3535
|
+
}
|
|
3536
|
+
renderTestOutcome(url, rule) {
|
|
3537
|
+
const names = this.testRuleNames(rule.groupId, rule.ruleId);
|
|
3538
|
+
const typeLabel = this.testRuleTypeLabel(rule.groupId, rule.ruleId);
|
|
3539
|
+
const outcome = this.document.createElement("p");
|
|
3540
|
+
outcome.dataset.testOutcome = "true";
|
|
3541
|
+
const lead = this.document.createElement("span");
|
|
3542
|
+
lead.textContent = names.groupKnown ? `${url} matches ${typeLabel} in ${names.groupName} / ` : `${url} matches ${typeLabel} in `;
|
|
3543
|
+
const ruleButton = this.createCommandButton(
|
|
3544
|
+
names.ruleLabel,
|
|
3545
|
+
"test:open-rule",
|
|
3546
|
+
false,
|
|
3547
|
+
{ groupId: rule.groupId, ruleId: rule.ruleId }
|
|
3548
|
+
);
|
|
3549
|
+
ruleButton.dataset.testRuleLink = "true";
|
|
3550
|
+
ruleButton.removeAttribute("data-btn");
|
|
3551
|
+
const tailParts = ["."];
|
|
3552
|
+
const preview = previewSentence(rule.actionPreview);
|
|
3553
|
+
if (preview) tailParts.push(` ${preview}`);
|
|
3554
|
+
if (rule.method.state === "not-applicable") {
|
|
3555
|
+
tailParts.push(" The method was not tested.");
|
|
3556
|
+
}
|
|
3557
|
+
if (rule.resourceType.state === "not-applicable") {
|
|
3558
|
+
tailParts.push(" The resource type was not tested.");
|
|
3559
|
+
}
|
|
3560
|
+
if (this.groupIsOffInBrowser(rule.groupId)) {
|
|
3561
|
+
tailParts.push(
|
|
3562
|
+
" This group is off in Chrome, so the browser will not apply this rule."
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
const tail = this.document.createElement("span");
|
|
3566
|
+
tail.textContent = tailParts.join("");
|
|
3567
|
+
outcome.append(lead, ruleButton, tail);
|
|
3568
|
+
return outcome;
|
|
3569
|
+
}
|
|
3570
|
+
renderTestMisses(misses) {
|
|
3571
|
+
const details = this.document.createElement("details");
|
|
3572
|
+
details.dataset.testMisses = "true";
|
|
3573
|
+
const summary = this.document.createElement("summary");
|
|
3574
|
+
summary.textContent = misses.length === 1 ? "1 rule did not match" : `${misses.length} rules did not match`;
|
|
3575
|
+
const list = this.document.createElement("ul");
|
|
3576
|
+
for (const rule of misses) {
|
|
3577
|
+
const names = this.testRuleNames(rule.groupId, rule.ruleId);
|
|
3578
|
+
const item = this.document.createElement("li");
|
|
3579
|
+
const ruleButton = this.createCommandButton(
|
|
3580
|
+
names.ruleLabel,
|
|
3581
|
+
"test:open-rule",
|
|
3582
|
+
false,
|
|
3583
|
+
{ groupId: rule.groupId, ruleId: rule.ruleId }
|
|
3584
|
+
);
|
|
3585
|
+
ruleButton.dataset.testRuleLink = "true";
|
|
3586
|
+
ruleButton.removeAttribute("data-btn");
|
|
3587
|
+
const reason = this.document.createElement("span");
|
|
3588
|
+
reason.dataset.testMissReason = "true";
|
|
3589
|
+
reason.textContent = missReason(rule);
|
|
3590
|
+
item.append(ruleButton, this.document.createTextNode(" \u2014 "), reason);
|
|
3591
|
+
list.append(item);
|
|
3592
|
+
}
|
|
3593
|
+
details.append(summary, list);
|
|
3594
|
+
return details;
|
|
3595
|
+
}
|
|
3412
3596
|
/**
|
|
3413
3597
|
* A test-result row is identified by the names the user actually sees. The dry
|
|
3414
3598
|
* result carries ids only, and the draft the editor handed over is the source
|
|
3415
|
-
* of truth for both
|
|
3416
|
-
* draft falls back to its id rather than rendering a blank row.
|
|
3599
|
+
* of truth for both. A rule that is no longer in the draft falls back to its id.
|
|
3417
3600
|
*/
|
|
3418
|
-
|
|
3601
|
+
testRuleNames(groupId, ruleId) {
|
|
3419
3602
|
const group = this.groupById(groupId);
|
|
3420
|
-
if (!group)
|
|
3603
|
+
if (!group) {
|
|
3604
|
+
return {
|
|
3605
|
+
groupKnown: false,
|
|
3606
|
+
ruleKnown: false,
|
|
3607
|
+
groupName: groupId,
|
|
3608
|
+
ruleLabel: `${groupId}/${ruleId}`
|
|
3609
|
+
};
|
|
3610
|
+
}
|
|
3421
3611
|
const groupName = displayName(group.name, "Unnamed group");
|
|
3422
3612
|
const rule = this.ruleById(groupId, ruleId);
|
|
3423
|
-
if (!rule)
|
|
3424
|
-
|
|
3613
|
+
if (!rule) {
|
|
3614
|
+
return {
|
|
3615
|
+
groupKnown: true,
|
|
3616
|
+
ruleKnown: false,
|
|
3617
|
+
groupName,
|
|
3618
|
+
ruleLabel: ruleId
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
return {
|
|
3622
|
+
groupKnown: true,
|
|
3623
|
+
ruleKnown: true,
|
|
3624
|
+
groupName,
|
|
3625
|
+
ruleLabel: displayName(rule.name, "Unnamed rule")
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
testRuleTypeLabel(groupId, ruleId) {
|
|
3629
|
+
const rule = this.ruleById(groupId, ruleId);
|
|
3630
|
+
const type = rule && typeof rule.type === "string" ? rule.type : "";
|
|
3631
|
+
return RULE_TYPE_LABELS[type] ?? "the rule";
|
|
3632
|
+
}
|
|
3633
|
+
groupIsOffInBrowser(groupId) {
|
|
3634
|
+
const enablement = this.options.groupEnablement;
|
|
3635
|
+
if (!enablement) return false;
|
|
3636
|
+
const group = this.groupById(groupId);
|
|
3637
|
+
const saved = group ? this.savedGroupId(group) : void 0;
|
|
3638
|
+
const id = saved && saved.length > 0 ? saved : groupId;
|
|
3639
|
+
return enablement.isEnabled(id) !== true;
|
|
3425
3640
|
}
|
|
3426
3641
|
navigate(route, groupId) {
|
|
3427
3642
|
if (route === "project") {
|
|
@@ -3732,17 +3947,6 @@ var EditorControllerImpl = class {
|
|
|
3732
3947
|
);
|
|
3733
3948
|
}
|
|
3734
3949
|
tools.append(this.createCommandButton("Validate", "validate", this.saving));
|
|
3735
|
-
if (this.route.kind === "test") {
|
|
3736
|
-
tools.append(
|
|
3737
|
-
this.createCommandButton(
|
|
3738
|
-
"Run test",
|
|
3739
|
-
"test:run",
|
|
3740
|
-
this.saving || this.testRunning,
|
|
3741
|
-
{},
|
|
3742
|
-
"primary"
|
|
3743
|
-
)
|
|
3744
|
-
);
|
|
3745
|
-
}
|
|
3746
3950
|
const commit = this.document.createElement("div");
|
|
3747
3951
|
commit.dataset.actionCluster = "commit";
|
|
3748
3952
|
commit.append(
|
|
@@ -3890,19 +4094,6 @@ var EditorControllerImpl = class {
|
|
|
3890
4094
|
this.createGroupActionClusters(groupId, groupName)
|
|
3891
4095
|
);
|
|
3892
4096
|
}
|
|
3893
|
-
} else {
|
|
3894
|
-
const run = this.document.createElement("div");
|
|
3895
|
-
run.dataset.actionCluster = "safe";
|
|
3896
|
-
run.append(
|
|
3897
|
-
this.createCommandButton(
|
|
3898
|
-
"Run test",
|
|
3899
|
-
"test:run",
|
|
3900
|
-
this.saving || this.testRunning,
|
|
3901
|
-
{},
|
|
3902
|
-
"primary"
|
|
3903
|
-
)
|
|
3904
|
-
);
|
|
3905
|
-
this.appendDockRow(dock, "Test", run);
|
|
3906
4097
|
}
|
|
3907
4098
|
this.appendDockRow(dock, "Project", this.createProjectActionRow());
|
|
3908
4099
|
this.form.append(dock);
|
|
@@ -4055,7 +4246,7 @@ var EditorControllerImpl = class {
|
|
|
4055
4246
|
this.form.append(heading);
|
|
4056
4247
|
const description = this.document.createElement("p");
|
|
4057
4248
|
description.dataset.testDescription = "true";
|
|
4058
|
-
description.textContent = "
|
|
4249
|
+
description.textContent = "Check whether these URLs match your rules. Nothing is contacted, and nothing is saved.";
|
|
4059
4250
|
this.form.append(description);
|
|
4060
4251
|
const panel = this.document.createElement("div");
|
|
4061
4252
|
panel.dataset.testPanel = "true";
|
|
@@ -4072,90 +4263,127 @@ var EditorControllerImpl = class {
|
|
|
4072
4263
|
urlsTextarea.placeholder = "https://example.com/page\nhttps://example.com/script.js\nhttps://other.com/";
|
|
4073
4264
|
urlsTextarea.value = this.testUrls;
|
|
4074
4265
|
urlsTextarea.dataset.testUrls = "true";
|
|
4266
|
+
urlsTextarea.dataset.editorKey = "test-urls";
|
|
4075
4267
|
urlsLabel.append(urlsTextarea);
|
|
4076
4268
|
urlsField.append(urlsLabel);
|
|
4077
4269
|
urlsFieldset.append(urlsField);
|
|
4270
|
+
const samples = this.renderSampleUrlButtons();
|
|
4271
|
+
if (samples) urlsFieldset.append(samples);
|
|
4078
4272
|
panel.append(urlsFieldset);
|
|
4079
|
-
const
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4273
|
+
const checking = this.document.createElement("p");
|
|
4274
|
+
checking.dataset.testChecking = "true";
|
|
4275
|
+
checking.textContent = checkingCopy(this.testMethod, this.testResourceType);
|
|
4276
|
+
panel.append(checking);
|
|
4083
4277
|
const defaultsGrid = this.document.createElement("div");
|
|
4084
4278
|
defaultsGrid.dataset.testDefaults = "true";
|
|
4085
4279
|
const methodField = this.document.createElement("div");
|
|
4086
4280
|
methodField.dataset.editorField = "true";
|
|
4087
4281
|
const methodLabel = this.document.createElement("label");
|
|
4088
|
-
methodLabel.textContent = "
|
|
4282
|
+
methodLabel.textContent = "Method";
|
|
4089
4283
|
const methodSelect = this.document.createElement("select");
|
|
4090
4284
|
methodSelect.dataset.testMethod = "true";
|
|
4285
|
+
methodSelect.dataset.editorKey = "test-method";
|
|
4286
|
+
for (const method of HTTP_METHODS2) {
|
|
4287
|
+
const option = this.document.createElement("option");
|
|
4288
|
+
option.value = method;
|
|
4289
|
+
option.textContent = method;
|
|
4290
|
+
methodSelect.append(option);
|
|
4291
|
+
}
|
|
4292
|
+
const methodAny = this.document.createElement("option");
|
|
4293
|
+
methodAny.value = "";
|
|
4294
|
+
methodAny.textContent = "Any method";
|
|
4295
|
+
methodSelect.append(methodAny);
|
|
4091
4296
|
methodSelect.value = this.testMethod;
|
|
4092
|
-
const methodEmpty = this.document.createElement("option");
|
|
4093
|
-
methodEmpty.value = "";
|
|
4094
|
-
methodEmpty.textContent = "(none \u2014 not-applicable)";
|
|
4095
|
-
methodSelect.append(methodEmpty);
|
|
4096
|
-
for (const m of HTTP_METHODS2) {
|
|
4097
|
-
const opt = this.document.createElement("option");
|
|
4098
|
-
opt.value = m;
|
|
4099
|
-
opt.textContent = m;
|
|
4100
|
-
methodSelect.append(opt);
|
|
4101
|
-
}
|
|
4102
4297
|
methodLabel.append(methodSelect);
|
|
4103
4298
|
methodField.append(methodLabel);
|
|
4104
4299
|
const rtField = this.document.createElement("div");
|
|
4105
4300
|
rtField.dataset.editorField = "true";
|
|
4106
4301
|
const rtLabel = this.document.createElement("label");
|
|
4107
|
-
rtLabel.textContent = "
|
|
4302
|
+
rtLabel.textContent = "Resource type";
|
|
4108
4303
|
const rtSelect = this.document.createElement("select");
|
|
4109
4304
|
rtSelect.dataset.testResourceType = "true";
|
|
4305
|
+
rtSelect.dataset.editorKey = "test-resource-type";
|
|
4306
|
+
for (const resourceType of RESOURCE_TYPES2) {
|
|
4307
|
+
const option = this.document.createElement("option");
|
|
4308
|
+
option.value = resourceType;
|
|
4309
|
+
option.textContent = RESOURCE_TYPE_LABELS[resourceType];
|
|
4310
|
+
rtSelect.append(option);
|
|
4311
|
+
}
|
|
4312
|
+
const rtAny = this.document.createElement("option");
|
|
4313
|
+
rtAny.value = "";
|
|
4314
|
+
rtAny.textContent = "Any resource type";
|
|
4315
|
+
rtSelect.append(rtAny);
|
|
4110
4316
|
rtSelect.value = this.testResourceType;
|
|
4111
|
-
const rtEmpty = this.document.createElement("option");
|
|
4112
|
-
rtEmpty.value = "";
|
|
4113
|
-
rtEmpty.textContent = "(none \u2014 not-applicable)";
|
|
4114
|
-
rtSelect.append(rtEmpty);
|
|
4115
|
-
for (const rt of RESOURCE_TYPES2) {
|
|
4116
|
-
const opt = this.document.createElement("option");
|
|
4117
|
-
opt.value = rt;
|
|
4118
|
-
opt.textContent = rt;
|
|
4119
|
-
rtSelect.append(opt);
|
|
4120
|
-
}
|
|
4121
4317
|
rtLabel.append(rtSelect);
|
|
4122
4318
|
rtField.append(rtLabel);
|
|
4123
4319
|
defaultsGrid.append(methodField, rtField);
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
maxCasesInput.min = "1";
|
|
4133
|
-
maxCasesInput.max = "10000";
|
|
4134
|
-
maxCasesInput.value = this.testMaxCases;
|
|
4135
|
-
maxCasesInput.style.width = "8rem";
|
|
4136
|
-
maxCasesInput.dataset.testMaxCases = "true";
|
|
4137
|
-
maxCasesLabel.append(maxCasesInput);
|
|
4138
|
-
maxCasesField.append(maxCasesLabel);
|
|
4139
|
-
panel.append(maxCasesField);
|
|
4140
|
-
const runBtn = this.createButton("Run test", "test:run");
|
|
4141
|
-
runBtn.type = "button";
|
|
4320
|
+
panel.append(defaultsGrid);
|
|
4321
|
+
const runBtn = this.createCommandButton(
|
|
4322
|
+
"Run test",
|
|
4323
|
+
"test:run",
|
|
4324
|
+
this.saving || this.testRunning,
|
|
4325
|
+
{},
|
|
4326
|
+
"primary"
|
|
4327
|
+
);
|
|
4142
4328
|
runBtn.dataset.testRun = "true";
|
|
4143
|
-
runBtn.dataset.command = "test:run";
|
|
4144
|
-
runBtn.dataset.btn = "primary";
|
|
4145
|
-
runBtn.disabled = this.saving || this.testRunning;
|
|
4146
4329
|
panel.append(runBtn);
|
|
4147
4330
|
this.form.append(panel);
|
|
4148
4331
|
const resultsSection = this.document.createElement("section");
|
|
4149
4332
|
resultsSection.dataset.testResults = "true";
|
|
4150
4333
|
this.form.append(resultsSection);
|
|
4151
|
-
if (this.
|
|
4334
|
+
if (this.ruleCount() === 0) {
|
|
4335
|
+
const empty = this.document.createElement("p");
|
|
4336
|
+
empty.dataset.testEmpty = "true";
|
|
4337
|
+
empty.textContent = "This project has no rules to test.";
|
|
4338
|
+
resultsSection.append(empty);
|
|
4339
|
+
} else if (this.testResult) {
|
|
4152
4340
|
this.renderTestResults(this.testResult);
|
|
4153
4341
|
} else if (this.testRunning) {
|
|
4154
4342
|
const pending = this.document.createElement("p");
|
|
4155
4343
|
pending.dataset.testPending = "true";
|
|
4156
|
-
pending.textContent = "
|
|
4344
|
+
pending.textContent = "Checking URLs...";
|
|
4157
4345
|
resultsSection.append(pending);
|
|
4346
|
+
} else if (this.statusMessage === "Only 256 URLs can be checked at once.") {
|
|
4347
|
+
const limit = this.document.createElement("p");
|
|
4348
|
+
limit.dataset.testLimit = "true";
|
|
4349
|
+
limit.textContent = this.statusMessage;
|
|
4350
|
+
resultsSection.append(limit);
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
renderSampleUrlButtons() {
|
|
4354
|
+
const samples = [];
|
|
4355
|
+
for (const group of this.draft.groups) {
|
|
4356
|
+
const groupId = safeText(group.id);
|
|
4357
|
+
for (const rule of group.rules) {
|
|
4358
|
+
const url = exactUrlFromSource(rule.source);
|
|
4359
|
+
if (!url) continue;
|
|
4360
|
+
samples.push({
|
|
4361
|
+
groupId,
|
|
4362
|
+
ruleId: safeText(rule.id),
|
|
4363
|
+
url,
|
|
4364
|
+
name: displayName(rule.name, "Unnamed rule")
|
|
4365
|
+
});
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
if (samples.length === 0) return void 0;
|
|
4369
|
+
const row = this.document.createElement("div");
|
|
4370
|
+
row.dataset.testSamples = "true";
|
|
4371
|
+
for (const sample of samples) {
|
|
4372
|
+
const button = this.createCommandButton(
|
|
4373
|
+
"Try a URL from this rule",
|
|
4374
|
+
"test:try-url",
|
|
4375
|
+
false,
|
|
4376
|
+
{ groupId: sample.groupId, ruleId: sample.ruleId, url: sample.url }
|
|
4377
|
+
);
|
|
4378
|
+
if (samples.length > 1) {
|
|
4379
|
+
button.setAttribute(
|
|
4380
|
+
"aria-label",
|
|
4381
|
+
`Try a URL from this rule, ${sample.name}`
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
row.append(button);
|
|
4158
4385
|
}
|
|
4386
|
+
return row;
|
|
4159
4387
|
}
|
|
4160
4388
|
renderRule(group, rule, groupIndex, ruleIndex) {
|
|
4161
4389
|
const groupId = safeText(group.id);
|
package/dist/node/index.js
CHANGED
|
@@ -26335,14 +26335,14 @@ function normalizeOptions(raw) {
|
|
|
26335
26335
|
}
|
|
26336
26336
|
maxCases = maxCasesProperty.value;
|
|
26337
26337
|
}
|
|
26338
|
-
let
|
|
26338
|
+
let previewAction;
|
|
26339
26339
|
if (previewProperty.present) {
|
|
26340
26340
|
if (previewProperty.value !== void 0 && typeof previewProperty.value !== "function") {
|
|
26341
26341
|
return { ok: false };
|
|
26342
26342
|
}
|
|
26343
|
-
|
|
26343
|
+
previewAction = previewProperty.value;
|
|
26344
26344
|
}
|
|
26345
|
-
return { ok: true, maxCases, previewAction
|
|
26345
|
+
return { ok: true, maxCases, previewAction };
|
|
26346
26346
|
}
|
|
26347
26347
|
function dryRunProject(operations, cases, options) {
|
|
26348
26348
|
const normalizedOptions = normalizeOptions(options);
|
|
@@ -26470,6 +26470,55 @@ function dryRunProject(operations, cases, options) {
|
|
|
26470
26470
|
}
|
|
26471
26471
|
};
|
|
26472
26472
|
}
|
|
26473
|
+
function captureSubject(matcher, url) {
|
|
26474
|
+
if (matcher.source.key === "url") return url;
|
|
26475
|
+
try {
|
|
26476
|
+
return new URL(url).hostname;
|
|
26477
|
+
} catch {
|
|
26478
|
+
return null;
|
|
26479
|
+
}
|
|
26480
|
+
}
|
|
26481
|
+
function previewRuleAction(operation, url) {
|
|
26482
|
+
const subject = captureSubject(operation.matcher, url);
|
|
26483
|
+
if (subject === null) return null;
|
|
26484
|
+
const captures = matchUrlCaptures(operation.matcher.source.value, subject);
|
|
26485
|
+
if (captures === null) return null;
|
|
26486
|
+
const expand = (value) => substituteUrlCaptures(value, captures);
|
|
26487
|
+
if (operation.kind === "redirect") {
|
|
26488
|
+
return {
|
|
26489
|
+
kind: "redirect",
|
|
26490
|
+
summary: expand(operation.redirect.destination)
|
|
26491
|
+
};
|
|
26492
|
+
}
|
|
26493
|
+
if (operation.kind === "query") {
|
|
26494
|
+
const values = operation.action.params.map(
|
|
26495
|
+
(param) => param.operation === "remove" ? `${param.name}=<removed>` : `${param.name}=${expand(param.value ?? "")}`
|
|
26496
|
+
).join(", ");
|
|
26497
|
+
return { kind: "query", summary: values };
|
|
26498
|
+
}
|
|
26499
|
+
if (operation.kind === "header") {
|
|
26500
|
+
if (operation.header.operation === "remove") {
|
|
26501
|
+
return { kind: "header", summary: `${operation.header.name}=<removed>` };
|
|
26502
|
+
}
|
|
26503
|
+
return {
|
|
26504
|
+
kind: "header",
|
|
26505
|
+
summary: `${operation.header.name}=${expand(operation.header.value ?? "")}`
|
|
26506
|
+
};
|
|
26507
|
+
}
|
|
26508
|
+
if (operation.kind === "request-body" && operation.requestBody.mode === "replace") {
|
|
26509
|
+
return {
|
|
26510
|
+
kind: operation.kind,
|
|
26511
|
+
summary: expand(operation.requestBody.body)
|
|
26512
|
+
};
|
|
26513
|
+
}
|
|
26514
|
+
if (operation.kind === "response-body" && "mode" in operation.responseBody && operation.responseBody.mode === "replace") {
|
|
26515
|
+
return {
|
|
26516
|
+
kind: operation.kind,
|
|
26517
|
+
summary: expand(operation.responseBody.body)
|
|
26518
|
+
};
|
|
26519
|
+
}
|
|
26520
|
+
return null;
|
|
26521
|
+
}
|
|
26473
26522
|
|
|
26474
26523
|
// packages/cli/src/server/routes.ts
|
|
26475
26524
|
function generateCsrfToken() {
|
|
@@ -26540,18 +26589,6 @@ function bodyErrorResponse(error) {
|
|
|
26540
26589
|
body: { code: "invalid-json", message: "Invalid JSON body" }
|
|
26541
26590
|
};
|
|
26542
26591
|
}
|
|
26543
|
-
function toMatcherOperations(operations) {
|
|
26544
|
-
return operations.map(
|
|
26545
|
-
({ groupId, ruleId, name, matcher, redactSensitiveInLogs }) => ({
|
|
26546
|
-
kind: "matcher",
|
|
26547
|
-
groupId,
|
|
26548
|
-
ruleId,
|
|
26549
|
-
name,
|
|
26550
|
-
matcher,
|
|
26551
|
-
redactSensitiveInLogs
|
|
26552
|
-
})
|
|
26553
|
-
);
|
|
26554
|
-
}
|
|
26555
26592
|
function isRecord2(value) {
|
|
26556
26593
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26557
26594
|
}
|
|
@@ -26864,10 +26901,13 @@ function createRoutes(context) {
|
|
|
26864
26901
|
);
|
|
26865
26902
|
return;
|
|
26866
26903
|
}
|
|
26904
|
+
const requested = isRecord2(body.options) && typeof body.options.maxCases === "number" && Number.isSafeInteger(body.options.maxCases) && body.options.maxCases > 0 ? body.options.maxCases : void 0;
|
|
26905
|
+
const options = { previewAction: previewRuleAction };
|
|
26906
|
+
if (requested !== void 0) options.maxCases = requested;
|
|
26867
26907
|
const result = dryRunProject(
|
|
26868
|
-
|
|
26908
|
+
compileResult.operations,
|
|
26869
26909
|
body.cases,
|
|
26870
|
-
|
|
26910
|
+
options
|
|
26871
26911
|
);
|
|
26872
26912
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
26873
26913
|
res.end(JSON.stringify(result));
|
|
@@ -27872,7 +27912,41 @@ function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
|
27872
27912
|
},
|
|
27873
27913
|
body: JSON.stringify({ project: currentProject, cases, options }),
|
|
27874
27914
|
});
|
|
27875
|
-
|
|
27915
|
+
let payload = null;
|
|
27916
|
+
try {
|
|
27917
|
+
payload = await res.json();
|
|
27918
|
+
} catch (error) {
|
|
27919
|
+
payload = null;
|
|
27920
|
+
}
|
|
27921
|
+
if (!res.ok) {
|
|
27922
|
+
const diagnostics = payload && Array.isArray(payload.diagnostics)
|
|
27923
|
+
? payload.diagnostics.map(function (diagnostic) {
|
|
27924
|
+
return {
|
|
27925
|
+
code: diagnostic && typeof diagnostic.code === 'string'
|
|
27926
|
+
? diagnostic.code
|
|
27927
|
+
: 'editor.validation-failed',
|
|
27928
|
+
severity: 'error',
|
|
27929
|
+
path: diagnostic && typeof diagnostic.path === 'string'
|
|
27930
|
+
? diagnostic.path
|
|
27931
|
+
: '',
|
|
27932
|
+
message: diagnostic && typeof diagnostic.message === 'string'
|
|
27933
|
+
? diagnostic.message
|
|
27934
|
+
: 'The project could not be tested.',
|
|
27935
|
+
};
|
|
27936
|
+
})
|
|
27937
|
+
: [{
|
|
27938
|
+
code: payload && typeof payload.code === 'string'
|
|
27939
|
+
? payload.code
|
|
27940
|
+
: 'editor.validation-failed',
|
|
27941
|
+
severity: 'error',
|
|
27942
|
+
path: '',
|
|
27943
|
+
message: payload && typeof payload.message === 'string'
|
|
27944
|
+
? payload.message
|
|
27945
|
+
: 'The project could not be tested.',
|
|
27946
|
+
}];
|
|
27947
|
+
return { ok: false, diagnostics: diagnostics };
|
|
27948
|
+
}
|
|
27949
|
+
return payload;
|
|
27876
27950
|
},
|
|
27877
27951
|
onCancel: () => {
|
|
27878
27952
|
cancel();
|
|
@@ -29947,7 +30021,7 @@ function errorMessage(error) {
|
|
|
29947
30021
|
// packages/cli/src/commands/runtime.ts
|
|
29948
30022
|
import { chmod, mkdir as mkdir4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
|
|
29949
30023
|
import { dirname as dirname7, join as join7, resolve as resolve6 } from "node:path";
|
|
29950
|
-
function
|
|
30024
|
+
function toMatcherOperations(operations) {
|
|
29951
30025
|
return operations.map(
|
|
29952
30026
|
({ groupId, ruleId, name, matcher, redactSensitiveInLogs }) => ({
|
|
29953
30027
|
kind: "matcher",
|
|
@@ -30239,7 +30313,7 @@ async function runtimeHostCommand(args, options = {}) {
|
|
|
30239
30313
|
const normalized = normalizeRuntimePreset({
|
|
30240
30314
|
version: 1,
|
|
30241
30315
|
limits: RUNTIME_LIMITS,
|
|
30242
|
-
matchers:
|
|
30316
|
+
matchers: toMatcherOperations(compileResult.operations),
|
|
30243
30317
|
grants: []
|
|
30244
30318
|
});
|
|
30245
30319
|
if (!normalized.ok) {
|
|
@@ -30284,55 +30358,6 @@ function usageError(message) {
|
|
|
30284
30358
|
return `Error: ${message}
|
|
30285
30359
|
`;
|
|
30286
30360
|
}
|
|
30287
|
-
function captureSubject(matcher, url) {
|
|
30288
|
-
if (matcher.source.key === "url") return url;
|
|
30289
|
-
try {
|
|
30290
|
-
return new URL(url).hostname;
|
|
30291
|
-
} catch {
|
|
30292
|
-
return null;
|
|
30293
|
-
}
|
|
30294
|
-
}
|
|
30295
|
-
function previewAction(operation, url) {
|
|
30296
|
-
const subject = captureSubject(operation.matcher, url);
|
|
30297
|
-
if (subject === null) return null;
|
|
30298
|
-
const captures = matchUrlCaptures(operation.matcher.source.value, subject);
|
|
30299
|
-
if (captures === null) return null;
|
|
30300
|
-
const expand = (value) => substituteUrlCaptures(value, captures);
|
|
30301
|
-
if (operation.kind === "redirect") {
|
|
30302
|
-
return {
|
|
30303
|
-
kind: "redirect",
|
|
30304
|
-
summary: expand(operation.redirect.destination)
|
|
30305
|
-
};
|
|
30306
|
-
}
|
|
30307
|
-
if (operation.kind === "query") {
|
|
30308
|
-
const values = operation.action.params.map(
|
|
30309
|
-
(param) => param.operation === "remove" ? `${param.name}=<removed>` : `${param.name}=${expand(param.value ?? "")}`
|
|
30310
|
-
).join(", ");
|
|
30311
|
-
return { kind: "query", summary: values };
|
|
30312
|
-
}
|
|
30313
|
-
if (operation.kind === "header") {
|
|
30314
|
-
if (operation.header.operation === "remove") {
|
|
30315
|
-
return { kind: "header", summary: `${operation.header.name}=<removed>` };
|
|
30316
|
-
}
|
|
30317
|
-
return {
|
|
30318
|
-
kind: "header",
|
|
30319
|
-
summary: `${operation.header.name}=${expand(operation.header.value ?? "")}`
|
|
30320
|
-
};
|
|
30321
|
-
}
|
|
30322
|
-
if (operation.kind === "request-body" && operation.requestBody.mode === "replace") {
|
|
30323
|
-
return {
|
|
30324
|
-
kind: operation.kind,
|
|
30325
|
-
summary: expand(operation.requestBody.body)
|
|
30326
|
-
};
|
|
30327
|
-
}
|
|
30328
|
-
if (operation.kind === "response-body" && "mode" in operation.responseBody && operation.responseBody.mode === "replace") {
|
|
30329
|
-
return {
|
|
30330
|
-
kind: operation.kind,
|
|
30331
|
-
summary: expand(operation.responseBody.body)
|
|
30332
|
-
};
|
|
30333
|
-
}
|
|
30334
|
-
return null;
|
|
30335
|
-
}
|
|
30336
30361
|
function isUrl(value) {
|
|
30337
30362
|
return parseTestUrl(value).ok;
|
|
30338
30363
|
}
|
|
@@ -30387,7 +30412,7 @@ function addDefaults(cases, defaultMethod, defaultResourceType) {
|
|
|
30387
30412
|
);
|
|
30388
30413
|
}
|
|
30389
30414
|
function resultOptions(maxCases) {
|
|
30390
|
-
const options = { previewAction };
|
|
30415
|
+
const options = { previewAction: previewRuleAction };
|
|
30391
30416
|
if (maxCases !== void 0) options.maxCases = maxCases;
|
|
30392
30417
|
return options;
|
|
30393
30418
|
}
|
package/package.json
CHANGED