@rogatio/cli 5.13.2 → 5.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/editor/index.js +107 -8
- package/dist/node/index.js +400 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ rogatio <command> [options]
|
|
|
40
40
|
| `rogatio verify [path]` | Validate a `.rogatio.json` file (schema + compiler). |
|
|
41
41
|
| `rogatio test [path] [url...]` | Run offline dry-run tests against `.rogatio.json`. |
|
|
42
42
|
| `rogatio ai <setup\|ls\|show\|delete\|test>` | AI provider configuration. `setup` interactive; `ls` list; `show` redacted; `delete` remove; `test` connection. |
|
|
43
|
-
| `rogatio runtime <install\|uninstall>` | Register the native-messaging host (and, on capable platforms, the device-local CA) in one transactional install; `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). |
|
|
43
|
+
| `rogatio runtime <install\|uninstall>` | Register the native-messaging host with `install --extension-id <id>` (and, on capable platforms, the device-local CA) in one transactional install; `uninstall` removes the host manifest, the device-local CA files, and the CA trust installation (idempotent). |
|
|
44
44
|
| `rogatio runtime host <path>` | Run the consolidated native-messaging host for the project (pair/authorize/body transforms over stdio). Normally launched by the browser; run manually only for debugging. |
|
|
45
45
|
|
|
46
46
|
Global options: `--help, -h` and `--version, -v`. Run `rogatio <command> --help`
|
package/dist/editor/index.js
CHANGED
|
@@ -2050,9 +2050,10 @@ var EditorControllerImpl = class {
|
|
|
2050
2050
|
this.instanceId = `rogatio-editor-${++editorInstanceCount}`;
|
|
2051
2051
|
this.draft = initial;
|
|
2052
2052
|
this.committed = cloneSnapshot(initial);
|
|
2053
|
-
|
|
2054
|
-
if (
|
|
2055
|
-
|
|
2053
|
+
this.errors = this.collectDiagnostics(this.draft);
|
|
2054
|
+
if (this.errors.length > 0) {
|
|
2055
|
+
this.statusMessage = `${this.errors.length} validation error${this.errors.length === 1 ? "" : "s"} found.`;
|
|
2056
|
+
this.focusRequest = this.errors[0]?.path;
|
|
2056
2057
|
}
|
|
2057
2058
|
if (options.aiAssist) {
|
|
2058
2059
|
this.aiAssistPanel = createAIAssistPanel(
|
|
@@ -2341,12 +2342,23 @@ var EditorControllerImpl = class {
|
|
|
2341
2342
|
if (groupId) this.requestRemoveGroup(groupId);
|
|
2342
2343
|
return;
|
|
2343
2344
|
}
|
|
2345
|
+
if (command === "copy-group") {
|
|
2346
|
+
const groupId = element.dataset.groupId;
|
|
2347
|
+
if (groupId) this.copyGroup(groupId);
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2344
2350
|
if (command === "remove-rule") {
|
|
2345
2351
|
const groupId = element.dataset.groupId;
|
|
2346
2352
|
const ruleId = element.dataset.ruleId;
|
|
2347
2353
|
if (groupId && ruleId) this.requestRemoveRule(groupId, ruleId);
|
|
2348
2354
|
return;
|
|
2349
2355
|
}
|
|
2356
|
+
if (command === "copy-rule") {
|
|
2357
|
+
const groupId = element.dataset.groupId;
|
|
2358
|
+
const ruleId = element.dataset.ruleId;
|
|
2359
|
+
if (groupId && ruleId) this.copyRule(groupId, ruleId);
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2350
2362
|
if (command === "add-group-origin") {
|
|
2351
2363
|
const groupId = element.dataset.groupId;
|
|
2352
2364
|
if (groupId) this.addGroupOrigin(groupId);
|
|
@@ -2604,13 +2616,24 @@ var EditorControllerImpl = class {
|
|
|
2604
2616
|
}
|
|
2605
2617
|
return ids;
|
|
2606
2618
|
}
|
|
2607
|
-
|
|
2608
|
-
const ids = this.allIds();
|
|
2619
|
+
allocateId(prefix, reserved) {
|
|
2609
2620
|
let candidate = prefix;
|
|
2610
2621
|
let suffix = 2;
|
|
2611
|
-
while (
|
|
2622
|
+
while (reserved.has(candidate)) candidate = `${prefix}-${suffix++}`;
|
|
2623
|
+
reserved.add(candidate);
|
|
2612
2624
|
return candidate;
|
|
2613
2625
|
}
|
|
2626
|
+
nextId(prefix) {
|
|
2627
|
+
return this.allocateId(prefix, this.allIds());
|
|
2628
|
+
}
|
|
2629
|
+
copyLabel(name) {
|
|
2630
|
+
const base = typeof name === "string" ? name : "";
|
|
2631
|
+
const suffix = " (copy)";
|
|
2632
|
+
if (base.length + suffix.length <= LIMITS.maxLabelLength) {
|
|
2633
|
+
return `${base}${suffix}`;
|
|
2634
|
+
}
|
|
2635
|
+
return base;
|
|
2636
|
+
}
|
|
2614
2637
|
addGroup() {
|
|
2615
2638
|
if (this.saving) return;
|
|
2616
2639
|
const groupId = this.nextId("group-new");
|
|
@@ -2652,6 +2675,65 @@ var EditorControllerImpl = class {
|
|
|
2652
2675
|
this.statusMessage = "Rule added.";
|
|
2653
2676
|
this.render();
|
|
2654
2677
|
}
|
|
2678
|
+
copyRule(groupId, ruleId) {
|
|
2679
|
+
if (this.saving) return;
|
|
2680
|
+
const group = this.groupById(groupId);
|
|
2681
|
+
if (!group) return;
|
|
2682
|
+
const sourceIndex = this.ruleIndex(group, ruleId);
|
|
2683
|
+
if (sourceIndex < 0) return;
|
|
2684
|
+
const snapshot = snapshotOwnData(group.rules[sourceIndex]);
|
|
2685
|
+
if (!snapshot.valid || typeof snapshot.value !== "object" || snapshot.value === null) {
|
|
2686
|
+
this.statusMessage = "Could not copy rule.";
|
|
2687
|
+
this.render();
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
2690
|
+
const copied = snapshot.value;
|
|
2691
|
+
copied.id = this.nextId("rule-new");
|
|
2692
|
+
copied.name = this.copyLabel(group.rules[sourceIndex].name);
|
|
2693
|
+
group.rules.splice(sourceIndex + 1, 0, copied);
|
|
2694
|
+
this.markChanged();
|
|
2695
|
+
this.route = { kind: "group", groupId };
|
|
2696
|
+
const groupIndex = this.groupIndex(groupId);
|
|
2697
|
+
this.focusRequest = pointer(
|
|
2698
|
+
"groups",
|
|
2699
|
+
groupIndex,
|
|
2700
|
+
"rules",
|
|
2701
|
+
sourceIndex + 1,
|
|
2702
|
+
"name"
|
|
2703
|
+
);
|
|
2704
|
+
this.statusMessage = "Rule copied.";
|
|
2705
|
+
this.render();
|
|
2706
|
+
}
|
|
2707
|
+
copyGroup(groupId) {
|
|
2708
|
+
if (this.saving) return;
|
|
2709
|
+
const sourceIndex = this.groupIndex(groupId);
|
|
2710
|
+
if (sourceIndex < 0) return;
|
|
2711
|
+
const source = this.draft.groups[sourceIndex];
|
|
2712
|
+
const snapshot = snapshotOwnData(source);
|
|
2713
|
+
if (!snapshot.valid || typeof snapshot.value !== "object" || snapshot.value === null) {
|
|
2714
|
+
this.statusMessage = "Could not copy group.";
|
|
2715
|
+
this.render();
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
const copied = snapshot.value;
|
|
2719
|
+
if (!Array.isArray(copied.rules)) {
|
|
2720
|
+
this.statusMessage = "Could not copy group.";
|
|
2721
|
+
this.render();
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2724
|
+
const reserved = this.allIds();
|
|
2725
|
+
copied.id = this.allocateId("group-new", reserved);
|
|
2726
|
+
copied.name = this.copyLabel(source.name);
|
|
2727
|
+
for (const rule of copied.rules) {
|
|
2728
|
+
rule.id = this.allocateId("rule-new", reserved);
|
|
2729
|
+
}
|
|
2730
|
+
this.draft.groups.splice(sourceIndex + 1, 0, copied);
|
|
2731
|
+
this.markChanged();
|
|
2732
|
+
this.route = { kind: "group", groupId: String(copied.id) };
|
|
2733
|
+
this.focusRequest = pointer("groups", sourceIndex + 1, "name");
|
|
2734
|
+
this.statusMessage = "Group copied.";
|
|
2735
|
+
this.render();
|
|
2736
|
+
}
|
|
2655
2737
|
moveGroup(groupId, delta) {
|
|
2656
2738
|
if (this.saving) return;
|
|
2657
2739
|
const index = this.groupIndex(groupId);
|
|
@@ -3367,6 +3449,13 @@ var EditorControllerImpl = class {
|
|
|
3367
3449
|
open.dataset.route = "group";
|
|
3368
3450
|
open.dataset.groupId = groupId;
|
|
3369
3451
|
open.dataset.btn = "secondary";
|
|
3452
|
+
const copy = this.createCommandButton(
|
|
3453
|
+
"Copy group",
|
|
3454
|
+
"copy-group",
|
|
3455
|
+
this.saving,
|
|
3456
|
+
{ groupId }
|
|
3457
|
+
);
|
|
3458
|
+
copy.setAttribute("aria-label", `Copy group ${groupName}`);
|
|
3370
3459
|
const remove = this.createCommandButton(
|
|
3371
3460
|
"Remove group",
|
|
3372
3461
|
"remove-group",
|
|
@@ -3375,7 +3464,7 @@ var EditorControllerImpl = class {
|
|
|
3375
3464
|
"danger"
|
|
3376
3465
|
);
|
|
3377
3466
|
remove.setAttribute("aria-label", `Remove group ${groupName}`);
|
|
3378
|
-
item.append(open, remove);
|
|
3467
|
+
item.append(open, copy, remove);
|
|
3379
3468
|
list.append(item);
|
|
3380
3469
|
}
|
|
3381
3470
|
groups.append(list);
|
|
@@ -3391,6 +3480,12 @@ var EditorControllerImpl = class {
|
|
|
3391
3480
|
headingRow.dataset.groupHeading = "true";
|
|
3392
3481
|
const heading = this.document.createElement("h2");
|
|
3393
3482
|
heading.textContent = groupName;
|
|
3483
|
+
const copyGroup = this.createCommandButton(
|
|
3484
|
+
"Copy group",
|
|
3485
|
+
"copy-group",
|
|
3486
|
+
this.saving,
|
|
3487
|
+
{ groupId }
|
|
3488
|
+
);
|
|
3394
3489
|
const removeGroup = this.createCommandButton(
|
|
3395
3490
|
"Remove group",
|
|
3396
3491
|
"remove-group",
|
|
@@ -3398,7 +3493,7 @@ var EditorControllerImpl = class {
|
|
|
3398
3493
|
{ groupId },
|
|
3399
3494
|
"danger"
|
|
3400
3495
|
);
|
|
3401
|
-
headingRow.append(heading, removeGroup);
|
|
3496
|
+
headingRow.append(heading, copyGroup, removeGroup);
|
|
3402
3497
|
this.form.append(headingRow);
|
|
3403
3498
|
const settings = this.document.createElement("fieldset");
|
|
3404
3499
|
settings.dataset.groupCard = "true";
|
|
@@ -3608,6 +3703,10 @@ var EditorControllerImpl = class {
|
|
|
3608
3703
|
this.saving || ruleIndex >= group.rules.length - 1,
|
|
3609
3704
|
{ groupId, ruleId }
|
|
3610
3705
|
),
|
|
3706
|
+
this.createCommandButton("Copy rule", "copy-rule", this.saving, {
|
|
3707
|
+
groupId,
|
|
3708
|
+
ruleId
|
|
3709
|
+
}),
|
|
3611
3710
|
this.createCommandButton(
|
|
3612
3711
|
"Remove rule",
|
|
3613
3712
|
"remove-rule",
|
package/dist/node/index.js
CHANGED
|
@@ -11,6 +11,117 @@ import * as path from "node:path";
|
|
|
11
11
|
|
|
12
12
|
// packages/schema/dist/node/index.js
|
|
13
13
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
14
|
+
function skipBalancedGroup(source, start) {
|
|
15
|
+
let depth = 0;
|
|
16
|
+
let index = start;
|
|
17
|
+
while (index < source.length) {
|
|
18
|
+
const char = source[index];
|
|
19
|
+
if (char === "\\") {
|
|
20
|
+
index += 2;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (char === "(") depth += 1;
|
|
24
|
+
else if (char === ")") {
|
|
25
|
+
depth -= 1;
|
|
26
|
+
if (depth === 0) return index;
|
|
27
|
+
}
|
|
28
|
+
index += 1;
|
|
29
|
+
}
|
|
30
|
+
return source.length;
|
|
31
|
+
}
|
|
32
|
+
function countCapturingGroups(urlRegex) {
|
|
33
|
+
let count = 0;
|
|
34
|
+
let index = 0;
|
|
35
|
+
while (index < urlRegex.length) {
|
|
36
|
+
const char = urlRegex[index];
|
|
37
|
+
if (char === "\\") {
|
|
38
|
+
index += 2;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (char === "(") {
|
|
42
|
+
if (urlRegex[index + 1] === "?") {
|
|
43
|
+
index = skipBalancedGroup(urlRegex, index);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
count += 1;
|
|
47
|
+
}
|
|
48
|
+
index += 1;
|
|
49
|
+
}
|
|
50
|
+
return count;
|
|
51
|
+
}
|
|
52
|
+
function validateCaptureTemplate(template, urlRegex) {
|
|
53
|
+
const groups = countCapturingGroups(urlRegex);
|
|
54
|
+
const issues = [];
|
|
55
|
+
for (let index = 0; index < template.length; index += 1) {
|
|
56
|
+
if (template[index] !== "$") continue;
|
|
57
|
+
const next = template[index + 1];
|
|
58
|
+
if (next === "$") {
|
|
59
|
+
index += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (next === void 0 || next < "0" || next > "9") {
|
|
63
|
+
issues.push({
|
|
64
|
+
code: "invalid-syntax",
|
|
65
|
+
message: "Capture references must use $1 through $9; use $$ for a literal $.",
|
|
66
|
+
offset: index,
|
|
67
|
+
groups
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const referenced = Number(next);
|
|
72
|
+
if (referenced === 0 || (template[index + 2] ?? "") >= "0" && (template[index + 2] ?? "") <= "9") {
|
|
73
|
+
issues.push({
|
|
74
|
+
code: "invalid-syntax",
|
|
75
|
+
message: "Capture references must use one digit: $1 through $9.",
|
|
76
|
+
offset: index,
|
|
77
|
+
referenced,
|
|
78
|
+
groups
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (referenced > groups) {
|
|
83
|
+
issues.push({
|
|
84
|
+
code: "out-of-range",
|
|
85
|
+
message: `Capture group $${referenced} is not defined by the URL pattern, which has ${groups} group${groups === 1 ? "" : "s"}.`,
|
|
86
|
+
offset: index,
|
|
87
|
+
referenced,
|
|
88
|
+
groups
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return issues;
|
|
93
|
+
}
|
|
94
|
+
function substituteUrlCaptures(template, captures) {
|
|
95
|
+
let output = "";
|
|
96
|
+
for (let index = 0; index < template.length; index += 1) {
|
|
97
|
+
const char = template[index];
|
|
98
|
+
if (char !== "$") {
|
|
99
|
+
output += char;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const next = template[index + 1];
|
|
103
|
+
if (next === "$") {
|
|
104
|
+
output += "$";
|
|
105
|
+
index += 1;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (next !== void 0 && next >= "1" && next <= "9") {
|
|
109
|
+
output += captures[Number(next) - 1] ?? "";
|
|
110
|
+
index += 1;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
output += "$";
|
|
114
|
+
}
|
|
115
|
+
return output;
|
|
116
|
+
}
|
|
117
|
+
function matchUrlCaptures(urlRegex, url) {
|
|
118
|
+
try {
|
|
119
|
+
const match = new RegExp(urlRegex).exec(url);
|
|
120
|
+
return match === null ? null : match.slice(1);
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
14
125
|
function hasControl(value) {
|
|
15
126
|
for (let index = 0; index < value.length; index += 1) {
|
|
16
127
|
const code = value.charCodeAt(index);
|
|
@@ -741,17 +852,25 @@ function semanticIssues(project) {
|
|
|
741
852
|
params: {}
|
|
742
853
|
});
|
|
743
854
|
} else {
|
|
855
|
+
const destinationPath = `${rulePath}/redirect/destination`;
|
|
744
856
|
for (const issue of validateRedirectDestination(
|
|
745
857
|
destination,
|
|
746
858
|
rule.urlRegex
|
|
747
859
|
)) {
|
|
748
860
|
issues.push({
|
|
749
|
-
instancePath:
|
|
861
|
+
instancePath: destinationPath,
|
|
750
862
|
keyword: issue.code,
|
|
751
863
|
message: issue.message,
|
|
752
864
|
params: {}
|
|
753
865
|
});
|
|
754
866
|
}
|
|
867
|
+
issues.push(
|
|
868
|
+
...captureValidationIssues(
|
|
869
|
+
destination,
|
|
870
|
+
rule.urlRegex,
|
|
871
|
+
destinationPath
|
|
872
|
+
)
|
|
873
|
+
);
|
|
755
874
|
}
|
|
756
875
|
}
|
|
757
876
|
const effectiveOrigins = /* @__PURE__ */ new Set();
|
|
@@ -797,6 +916,14 @@ function semanticIssues(project) {
|
|
|
797
916
|
message: "query param value is required for set",
|
|
798
917
|
params: {}
|
|
799
918
|
});
|
|
919
|
+
} else {
|
|
920
|
+
issues.push(
|
|
921
|
+
...captureValidationIssues(
|
|
922
|
+
param.value,
|
|
923
|
+
rule.urlRegex,
|
|
924
|
+
`${rulePath}/action/params/${p}/value`
|
|
925
|
+
)
|
|
926
|
+
);
|
|
800
927
|
}
|
|
801
928
|
} else if (param.value !== void 0) {
|
|
802
929
|
issues.push({
|
|
@@ -821,6 +948,15 @@ function semanticIssues(project) {
|
|
|
821
948
|
}
|
|
822
949
|
});
|
|
823
950
|
}
|
|
951
|
+
if ((rule.headerOperation ?? "set") !== "remove" && typeof rule.headerValue === "string") {
|
|
952
|
+
issues.push(
|
|
953
|
+
...captureValidationIssues(
|
|
954
|
+
rule.headerValue,
|
|
955
|
+
rule.urlRegex,
|
|
956
|
+
`${rulePath}/headerValue`
|
|
957
|
+
)
|
|
958
|
+
);
|
|
959
|
+
}
|
|
824
960
|
}
|
|
825
961
|
if (rule.type === "response-body") {
|
|
826
962
|
const action2 = rule.responseBody;
|
|
@@ -854,6 +990,14 @@ function semanticIssues(project) {
|
|
|
854
990
|
message: "Replace body must not contain lone UTF-16 surrogates.",
|
|
855
991
|
params: {}
|
|
856
992
|
});
|
|
993
|
+
} else {
|
|
994
|
+
issues.push(
|
|
995
|
+
...captureValidationIssues(
|
|
996
|
+
action2.body,
|
|
997
|
+
rule.urlRegex,
|
|
998
|
+
`${actionPath}/body`
|
|
999
|
+
)
|
|
1000
|
+
);
|
|
857
1001
|
}
|
|
858
1002
|
} else if ("replacements" in action2 && Array.isArray(action2.replacements)) {
|
|
859
1003
|
if (action2.replacements.length === 0) {
|
|
@@ -927,6 +1071,14 @@ function semanticIssues(project) {
|
|
|
927
1071
|
message: "Replace body must not contain lone UTF-16 surrogates.",
|
|
928
1072
|
params: {}
|
|
929
1073
|
});
|
|
1074
|
+
} else {
|
|
1075
|
+
issues.push(
|
|
1076
|
+
...captureValidationIssues(
|
|
1077
|
+
action2.body,
|
|
1078
|
+
rule.urlRegex,
|
|
1079
|
+
`${actionPath}/body`
|
|
1080
|
+
)
|
|
1081
|
+
);
|
|
930
1082
|
}
|
|
931
1083
|
}
|
|
932
1084
|
if (action2.mode === "regex") {
|
|
@@ -1059,7 +1211,19 @@ function semanticIssues(project) {
|
|
|
1059
1211
|
}
|
|
1060
1212
|
return issues;
|
|
1061
1213
|
}
|
|
1062
|
-
function
|
|
1214
|
+
function captureValidationIssues(value, urlRegex, instancePath) {
|
|
1215
|
+
return validateCaptureTemplate(value, urlRegex).map((issue) => ({
|
|
1216
|
+
instancePath,
|
|
1217
|
+
keyword: `schema.capture-${issue.code}`,
|
|
1218
|
+
message: issue.message,
|
|
1219
|
+
params: {
|
|
1220
|
+
offset: issue.offset,
|
|
1221
|
+
groups: issue.groups,
|
|
1222
|
+
...issue.referenced === void 0 ? {} : { referenced: issue.referenced }
|
|
1223
|
+
}
|
|
1224
|
+
}));
|
|
1225
|
+
}
|
|
1226
|
+
function skipBalancedGroup2(source, start) {
|
|
1063
1227
|
let depth = 0;
|
|
1064
1228
|
let index = start;
|
|
1065
1229
|
const length = source.length;
|
|
@@ -1079,7 +1243,7 @@ function skipBalancedGroup(source, start) {
|
|
|
1079
1243
|
}
|
|
1080
1244
|
return length;
|
|
1081
1245
|
}
|
|
1082
|
-
function
|
|
1246
|
+
function countCapturingGroups2(urlRegex) {
|
|
1083
1247
|
let count = 0;
|
|
1084
1248
|
let index = 0;
|
|
1085
1249
|
const length = urlRegex.length;
|
|
@@ -1091,7 +1255,7 @@ function countCapturingGroups(urlRegex) {
|
|
|
1091
1255
|
}
|
|
1092
1256
|
if (char === "(") {
|
|
1093
1257
|
if (urlRegex[index + 1] === "?") {
|
|
1094
|
-
index =
|
|
1258
|
+
index = skipBalancedGroup2(urlRegex, index);
|
|
1095
1259
|
continue;
|
|
1096
1260
|
}
|
|
1097
1261
|
count += 1;
|
|
@@ -1147,7 +1311,7 @@ function validateRedirectDestination(destination, urlRegex) {
|
|
|
1147
1311
|
});
|
|
1148
1312
|
return issues;
|
|
1149
1313
|
}
|
|
1150
|
-
const groups =
|
|
1314
|
+
const groups = countCapturingGroups2(urlRegex);
|
|
1151
1315
|
const backreference = /\\([1-9])/g;
|
|
1152
1316
|
let match = backreference.exec(destination);
|
|
1153
1317
|
while (match !== null) {
|
|
@@ -20717,6 +20881,7 @@ function authorizeExact(preset, value) {
|
|
|
20717
20881
|
kind: grant.kind,
|
|
20718
20882
|
target: grant.target,
|
|
20719
20883
|
method: grant.method,
|
|
20884
|
+
urlRegex: matcher.matcher.urlRegex.source,
|
|
20720
20885
|
presetDigest: preset.digest
|
|
20721
20886
|
})
|
|
20722
20887
|
};
|
|
@@ -23383,14 +23548,14 @@ function normalizeOptions(raw) {
|
|
|
23383
23548
|
}
|
|
23384
23549
|
maxCases = maxCasesProperty.value;
|
|
23385
23550
|
}
|
|
23386
|
-
let
|
|
23551
|
+
let previewAction2;
|
|
23387
23552
|
if (previewProperty.present) {
|
|
23388
23553
|
if (previewProperty.value !== void 0 && typeof previewProperty.value !== "function") {
|
|
23389
23554
|
return { ok: false };
|
|
23390
23555
|
}
|
|
23391
|
-
|
|
23556
|
+
previewAction2 = previewProperty.value;
|
|
23392
23557
|
}
|
|
23393
|
-
return { ok: true, maxCases, previewAction };
|
|
23558
|
+
return { ok: true, maxCases, previewAction: previewAction2 };
|
|
23394
23559
|
}
|
|
23395
23560
|
function dryRunProject(operations, cases, options) {
|
|
23396
23561
|
const normalizedOptions = normalizeOptions(options);
|
|
@@ -24163,7 +24328,71 @@ var ProjectFileError = class extends ProjectStorageError {
|
|
|
24163
24328
|
}
|
|
24164
24329
|
};
|
|
24165
24330
|
function emptyProjectDocument() {
|
|
24166
|
-
return { version: 1, name:
|
|
24331
|
+
return { version: 1, name: randomCivilizationProjectName(), groups: [] };
|
|
24332
|
+
}
|
|
24333
|
+
var PROJECT_NAME_LEADS = Object.freeze([
|
|
24334
|
+
"Apollo",
|
|
24335
|
+
"Atlas",
|
|
24336
|
+
"Aurora",
|
|
24337
|
+
"Beacon",
|
|
24338
|
+
"Chronos",
|
|
24339
|
+
"Cosmos",
|
|
24340
|
+
"Dawn",
|
|
24341
|
+
"Epoch",
|
|
24342
|
+
"Genesis",
|
|
24343
|
+
"Helios",
|
|
24344
|
+
"Horizon",
|
|
24345
|
+
"Lumen",
|
|
24346
|
+
"Meridian",
|
|
24347
|
+
"Nova",
|
|
24348
|
+
"Olympus",
|
|
24349
|
+
"Orion",
|
|
24350
|
+
"Pax",
|
|
24351
|
+
"Prometheus",
|
|
24352
|
+
"Quantum",
|
|
24353
|
+
"Solaris",
|
|
24354
|
+
"Summit",
|
|
24355
|
+
"Terra",
|
|
24356
|
+
"Titan",
|
|
24357
|
+
"Verdant",
|
|
24358
|
+
"Zenith"
|
|
24359
|
+
]);
|
|
24360
|
+
var PROJECT_NAME_TAILS = Object.freeze([
|
|
24361
|
+
"Accord",
|
|
24362
|
+
"Archive",
|
|
24363
|
+
"Ascension",
|
|
24364
|
+
"Bridge",
|
|
24365
|
+
"Charter",
|
|
24366
|
+
"Circuit",
|
|
24367
|
+
"Codex",
|
|
24368
|
+
"Continuum",
|
|
24369
|
+
"Covenant",
|
|
24370
|
+
"Engine",
|
|
24371
|
+
"Fabric",
|
|
24372
|
+
"Foundation",
|
|
24373
|
+
"Grid",
|
|
24374
|
+
"Lattice",
|
|
24375
|
+
"Legacy",
|
|
24376
|
+
"Mandate",
|
|
24377
|
+
"Nexus",
|
|
24378
|
+
"Orbit",
|
|
24379
|
+
"Protocol",
|
|
24380
|
+
"Relay",
|
|
24381
|
+
"Renaissance",
|
|
24382
|
+
"Spire",
|
|
24383
|
+
"Threshold",
|
|
24384
|
+
"Vault",
|
|
24385
|
+
"Weave"
|
|
24386
|
+
]);
|
|
24387
|
+
function randomCivilizationProjectName() {
|
|
24388
|
+
const bytes = randomBytes4(2);
|
|
24389
|
+
const lead = pickWord(PROJECT_NAME_LEADS, bytes[0] ?? 0);
|
|
24390
|
+
const tail = pickWord(PROJECT_NAME_TAILS, bytes[1] ?? 0);
|
|
24391
|
+
return `${lead} ${tail}`;
|
|
24392
|
+
}
|
|
24393
|
+
function pickWord(words, byte) {
|
|
24394
|
+
const word = words[byte % words.length];
|
|
24395
|
+
return word ?? words[0] ?? "Nova";
|
|
24167
24396
|
}
|
|
24168
24397
|
function isRogatioProjectFilename(name) {
|
|
24169
24398
|
return name === ".rogatio.json" || name.endsWith(".rogatio.json");
|
|
@@ -24443,7 +24672,7 @@ async function editCommand(args, options = {}) {
|
|
|
24443
24672
|
if (e instanceof ProjectStorageError && e.code === "not-found") {
|
|
24444
24673
|
try {
|
|
24445
24674
|
await storage.create({ id: filePath });
|
|
24446
|
-
projectData =
|
|
24675
|
+
projectData = await storage.get(filePath);
|
|
24447
24676
|
} catch (createError) {
|
|
24448
24677
|
console.error(`Error writing initial project: ${createError}`);
|
|
24449
24678
|
return { exitCode: Promise.resolve(2), shutdown: () => {
|
|
@@ -24455,6 +24684,17 @@ async function editCommand(args, options = {}) {
|
|
|
24455
24684
|
} };
|
|
24456
24685
|
}
|
|
24457
24686
|
}
|
|
24687
|
+
try {
|
|
24688
|
+
projectData = await ensureCivilizationProjectName(
|
|
24689
|
+
storage,
|
|
24690
|
+
filePath,
|
|
24691
|
+
projectData
|
|
24692
|
+
);
|
|
24693
|
+
} catch (e) {
|
|
24694
|
+
console.error(`Error: ${e}`);
|
|
24695
|
+
return { exitCode: Promise.resolve(2), shutdown: () => {
|
|
24696
|
+
} };
|
|
24697
|
+
}
|
|
24458
24698
|
let aiClient;
|
|
24459
24699
|
const providerConfig = await readProviderConfig();
|
|
24460
24700
|
if (providerConfig) {
|
|
@@ -24544,6 +24784,26 @@ async function editCommand(args, options = {}) {
|
|
|
24544
24784
|
shutdown
|
|
24545
24785
|
};
|
|
24546
24786
|
}
|
|
24787
|
+
function needsCivilizationProjectName(data) {
|
|
24788
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
24789
|
+
return false;
|
|
24790
|
+
}
|
|
24791
|
+
if (!Object.hasOwn(data, "name")) return true;
|
|
24792
|
+
const name = data.name;
|
|
24793
|
+
return typeof name !== "string" || name.trim() === "";
|
|
24794
|
+
}
|
|
24795
|
+
async function ensureCivilizationProjectName(storage, filePath, data) {
|
|
24796
|
+
if (!needsCivilizationProjectName(data)) return data;
|
|
24797
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
24798
|
+
return data;
|
|
24799
|
+
}
|
|
24800
|
+
const named = {
|
|
24801
|
+
...data,
|
|
24802
|
+
name: randomCivilizationProjectName()
|
|
24803
|
+
};
|
|
24804
|
+
await storage.update(filePath, named);
|
|
24805
|
+
return named;
|
|
24806
|
+
}
|
|
24547
24807
|
function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
24548
24808
|
const aiAssistHandler = aiConfigured ? `
|
|
24549
24809
|
aiAssist: async function*(request) {
|
|
@@ -24677,41 +24937,97 @@ function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
|
24677
24937
|
|
|
24678
24938
|
const project = await fetchProject();
|
|
24679
24939
|
|
|
24680
|
-
|
|
24681
|
-
|
|
24682
|
-
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
|
|
24686
|
-
|
|
24687
|
-
|
|
24688
|
-
|
|
24689
|
-
|
|
24690
|
-
|
|
24691
|
-
|
|
24692
|
-
|
|
24693
|
-
|
|
24694
|
-
|
|
24695
|
-
|
|
24940
|
+
try {
|
|
24941
|
+
const editor = createEditor({
|
|
24942
|
+
root,
|
|
24943
|
+
initialProject: project,
|
|
24944
|
+
// Host validate is sync by contract; use sync XHR on loopback.
|
|
24945
|
+
validate: (value) => {
|
|
24946
|
+
const xhr = new XMLHttpRequest();
|
|
24947
|
+
xhr.open('POST', apiBase + '/api/validate', false);
|
|
24948
|
+
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
24949
|
+
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
|
|
24950
|
+
try {
|
|
24951
|
+
xhr.send(JSON.stringify(value));
|
|
24952
|
+
} catch {
|
|
24953
|
+
return [{
|
|
24954
|
+
code: 'editor.validation-failed',
|
|
24955
|
+
severity: 'error',
|
|
24956
|
+
path: '',
|
|
24957
|
+
message: 'Project validation could not be completed.',
|
|
24958
|
+
}];
|
|
24959
|
+
}
|
|
24960
|
+
if (xhr.status < 200 || xhr.status >= 300) {
|
|
24961
|
+
return [{
|
|
24962
|
+
code: 'editor.validation-failed',
|
|
24963
|
+
severity: 'error',
|
|
24964
|
+
path: '',
|
|
24965
|
+
message: 'Project validation could not be completed.',
|
|
24966
|
+
}];
|
|
24967
|
+
}
|
|
24968
|
+
let result;
|
|
24969
|
+
try {
|
|
24970
|
+
result = JSON.parse(xhr.responseText);
|
|
24971
|
+
} catch {
|
|
24972
|
+
return [{
|
|
24973
|
+
code: 'editor.validation-failed',
|
|
24974
|
+
severity: 'error',
|
|
24975
|
+
path: '',
|
|
24976
|
+
message: 'Project validation could not be completed.',
|
|
24977
|
+
}];
|
|
24978
|
+
}
|
|
24979
|
+
const diagnostics = Array.isArray(result.diagnostics)
|
|
24980
|
+
? result.diagnostics
|
|
24981
|
+
: [];
|
|
24982
|
+
return diagnostics.map((d) => ({
|
|
24983
|
+
code: d.code,
|
|
24984
|
+
severity: d.severity,
|
|
24985
|
+
path: d.path,
|
|
24986
|
+
message: d.message,
|
|
24987
|
+
}));
|
|
24988
|
+
},
|
|
24989
|
+
save: async (project) => {
|
|
24990
|
+
const result = await saveProject(project);
|
|
24991
|
+
if (result.ok) {
|
|
24992
|
+
return { ok: true };
|
|
24993
|
+
}
|
|
24994
|
+
return { ok: false, code: result.code, message: result.message };
|
|
24995
|
+
},
|
|
24996
|
+
dryRun: async (currentProject, cases, options) => {
|
|
24997
|
+
const res = await fetch(apiBase + '/api/dry-run', {
|
|
24998
|
+
method: 'POST',
|
|
24999
|
+
headers: {
|
|
25000
|
+
'Content-Type': 'application/json',
|
|
25001
|
+
'X-CSRF-Token': csrfToken,
|
|
25002
|
+
},
|
|
25003
|
+
body: JSON.stringify({ project: currentProject, cases, options }),
|
|
25004
|
+
});
|
|
25005
|
+
return res.json();
|
|
25006
|
+
},
|
|
25007
|
+
onCancel: () => {
|
|
25008
|
+
cancel();
|
|
25009
|
+
},
|
|
25010
|
+
${aiAssistHandler}
|
|
25011
|
+
});
|
|
25012
|
+
void editor;
|
|
25013
|
+
} catch (error) {
|
|
25014
|
+
const parts = [];
|
|
25015
|
+
if (error && typeof error === 'object') {
|
|
25016
|
+
if ('message' in error) parts.push(String(error.message));
|
|
25017
|
+
if (Array.isArray(error.diagnostics)) {
|
|
25018
|
+
for (const diagnostic of error.diagnostics) {
|
|
25019
|
+
if (
|
|
25020
|
+
diagnostic &&
|
|
25021
|
+
typeof diagnostic === 'object' &&
|
|
25022
|
+
typeof diagnostic.message === 'string'
|
|
25023
|
+
) {
|
|
25024
|
+
parts.push(diagnostic.message);
|
|
25025
|
+
}
|
|
25026
|
+
}
|
|
24696
25027
|
}
|
|
24697
|
-
|
|
24698
|
-
|
|
24699
|
-
|
|
24700
|
-
const res = await fetch(apiBase + '/api/dry-run', {
|
|
24701
|
-
method: 'POST',
|
|
24702
|
-
headers: {
|
|
24703
|
-
'Content-Type': 'application/json',
|
|
24704
|
-
'X-CSRF-Token': csrfToken,
|
|
24705
|
-
},
|
|
24706
|
-
body: JSON.stringify({ project: currentProject, cases, options }),
|
|
24707
|
-
});
|
|
24708
|
-
return res.json();
|
|
24709
|
-
},
|
|
24710
|
-
onCancel: () => {
|
|
24711
|
-
cancel();
|
|
24712
|
-
},
|
|
24713
|
-
${aiAssistHandler}
|
|
24714
|
-
});
|
|
25028
|
+
}
|
|
25029
|
+
root.textContent = parts.join(' \u2014 ') || 'Rogatio editor could not initialize';
|
|
25030
|
+
}
|
|
24715
25031
|
</script>
|
|
24716
25032
|
</body>
|
|
24717
25033
|
</html>`;
|
|
@@ -25031,17 +25347,44 @@ function usageError(message) {
|
|
|
25031
25347
|
return `Error: ${message}
|
|
25032
25348
|
`;
|
|
25033
25349
|
}
|
|
25034
|
-
function
|
|
25035
|
-
|
|
25036
|
-
|
|
25037
|
-
|
|
25038
|
-
|
|
25039
|
-
|
|
25040
|
-
|
|
25041
|
-
|
|
25042
|
-
|
|
25043
|
-
|
|
25044
|
-
)
|
|
25350
|
+
function previewAction(operation, url) {
|
|
25351
|
+
const captures = matchUrlCaptures(operation.matcher.urlRegex.source, url);
|
|
25352
|
+
if (captures === null) return null;
|
|
25353
|
+
const expand = (value) => substituteUrlCaptures(value, captures);
|
|
25354
|
+
if (operation.kind === "redirect") {
|
|
25355
|
+
return {
|
|
25356
|
+
kind: "redirect",
|
|
25357
|
+
summary: expand(operation.redirect.destination)
|
|
25358
|
+
};
|
|
25359
|
+
}
|
|
25360
|
+
if (operation.kind === "query") {
|
|
25361
|
+
const values = operation.action.params.map(
|
|
25362
|
+
(param) => param.operation === "remove" ? `${param.name}=<removed>` : `${param.name}=${expand(param.value ?? "")}`
|
|
25363
|
+
).join(", ");
|
|
25364
|
+
return { kind: "query", summary: values };
|
|
25365
|
+
}
|
|
25366
|
+
if (operation.kind === "header") {
|
|
25367
|
+
if (operation.header.operation === "remove") {
|
|
25368
|
+
return { kind: "header", summary: `${operation.header.name}=<removed>` };
|
|
25369
|
+
}
|
|
25370
|
+
return {
|
|
25371
|
+
kind: "header",
|
|
25372
|
+
summary: `${operation.header.name}=${expand(operation.header.value ?? "")}`
|
|
25373
|
+
};
|
|
25374
|
+
}
|
|
25375
|
+
if (operation.kind === "request-body" && operation.requestBody.mode === "replace") {
|
|
25376
|
+
return {
|
|
25377
|
+
kind: operation.kind,
|
|
25378
|
+
summary: expand(operation.requestBody.body)
|
|
25379
|
+
};
|
|
25380
|
+
}
|
|
25381
|
+
if (operation.kind === "response-body" && "mode" in operation.responseBody && operation.responseBody.mode === "replace") {
|
|
25382
|
+
return {
|
|
25383
|
+
kind: operation.kind,
|
|
25384
|
+
summary: expand(operation.responseBody.body)
|
|
25385
|
+
};
|
|
25386
|
+
}
|
|
25387
|
+
return null;
|
|
25045
25388
|
}
|
|
25046
25389
|
function isUrl(value) {
|
|
25047
25390
|
return parseTestUrl(value).ok;
|
|
@@ -25097,7 +25440,7 @@ function addDefaults(cases, defaultMethod, defaultResourceType) {
|
|
|
25097
25440
|
);
|
|
25098
25441
|
}
|
|
25099
25442
|
function resultOptions(maxCases) {
|
|
25100
|
-
const options = {};
|
|
25443
|
+
const options = { previewAction };
|
|
25101
25444
|
if (maxCases !== void 0) options.maxCases = maxCases;
|
|
25102
25445
|
return options;
|
|
25103
25446
|
}
|
|
@@ -25293,7 +25636,7 @@ async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
|
|
|
25293
25636
|
defaultResourceType
|
|
25294
25637
|
);
|
|
25295
25638
|
const dryRunResult = dryRunProject(
|
|
25296
|
-
|
|
25639
|
+
compileResult.operations,
|
|
25297
25640
|
testCases,
|
|
25298
25641
|
resultOptions(maxCases)
|
|
25299
25642
|
);
|
package/package.json
CHANGED