@rogatio/cli 6.6.4 → 6.7.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 -0
- package/dist/node/index.js +2377 -275
- package/package.json +2 -1
package/dist/node/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// packages/cli/src/index.ts
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
|
-
import { basename as basename3, dirname as dirname8, resolve as
|
|
5
|
+
import { basename as basename3, dirname as dirname8, resolve as resolve9 } from "node:path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7
7
|
|
|
8
8
|
// packages/runtime/dist/node/index.js
|
|
@@ -219,6 +219,7 @@ var LIMITS = Object.freeze({
|
|
|
219
219
|
maxRequestBodyOperations: 32,
|
|
220
220
|
maxLocalOrigins: 32
|
|
221
221
|
});
|
|
222
|
+
var NON_ALPHANUMERIC = /[^a-z0-9]+/u;
|
|
222
223
|
var WHITESPACE_RUN = /\s+/gu;
|
|
223
224
|
var FALLBACK_PREFIX = Object.freeze({
|
|
224
225
|
group: "Group",
|
|
@@ -228,6 +229,46 @@ function normalizeNameKey(name) {
|
|
|
228
229
|
if (typeof name !== "string") return "";
|
|
229
230
|
return name.trim().replace(WHITESPACE_RUN, " ").toLowerCase();
|
|
230
231
|
}
|
|
232
|
+
function pascalCase(value) {
|
|
233
|
+
const tokens = value.toLowerCase().split(NON_ALPHANUMERIC);
|
|
234
|
+
let result = "";
|
|
235
|
+
for (const token of tokens) {
|
|
236
|
+
const first = token.charAt(0);
|
|
237
|
+
if (first.length === 0) continue;
|
|
238
|
+
result += first.toUpperCase() + token.slice(1);
|
|
239
|
+
}
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
function allocate(stem, reserved, maxLength) {
|
|
243
|
+
for (let suffix = 1; ; suffix += 1) {
|
|
244
|
+
const suffixText = suffix === 1 ? "" : String(suffix);
|
|
245
|
+
const room = Math.max(1, maxLength - suffixText.length);
|
|
246
|
+
const candidate = `${stem.slice(0, room)}${suffixText}`;
|
|
247
|
+
if (reserved.has(candidate)) continue;
|
|
248
|
+
reserved.add(candidate);
|
|
249
|
+
return candidate;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function deriveEntityId(name, kind, reserved) {
|
|
253
|
+
const derived = pascalCase(typeof name === "string" ? name : "");
|
|
254
|
+
return allocate(
|
|
255
|
+
derived.length > 0 ? derived : FALLBACK_PREFIX[kind],
|
|
256
|
+
reserved,
|
|
257
|
+
LIMITS.maxIdLength
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
function uniqueName(base, reserved) {
|
|
261
|
+
const label2 = typeof base === "string" ? base : "";
|
|
262
|
+
for (let suffix = 1; ; suffix += 1) {
|
|
263
|
+
const suffixText = suffix === 1 ? "" : ` ${suffix}`;
|
|
264
|
+
const room = Math.max(1, LIMITS.maxLabelLength - suffixText.length);
|
|
265
|
+
const candidate = `${label2.slice(0, room)}${suffixText}`;
|
|
266
|
+
const key = normalizeNameKey(candidate);
|
|
267
|
+
if (reserved.has(key)) continue;
|
|
268
|
+
reserved.add(key);
|
|
269
|
+
return candidate;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
231
272
|
function normalizeSiteOrigin(value) {
|
|
232
273
|
if (typeof value !== "string" || value.length === 0 || value.trim() !== value)
|
|
233
274
|
return null;
|
|
@@ -1023,7 +1064,7 @@ function snapshotOwnData(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1023
1064
|
return { valid: false };
|
|
1024
1065
|
}
|
|
1025
1066
|
}
|
|
1026
|
-
const
|
|
1067
|
+
const snapshot22 = new Array(length);
|
|
1027
1068
|
for (let index = 0; index < length; index += 1) {
|
|
1028
1069
|
const descriptor = Object.getOwnPropertyDescriptor(
|
|
1029
1070
|
value,
|
|
@@ -1034,11 +1075,11 @@ function snapshotOwnData(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1034
1075
|
}
|
|
1035
1076
|
const child = snapshotOwnData(descriptor.value, ancestors);
|
|
1036
1077
|
if (!child.valid) return child;
|
|
1037
|
-
|
|
1078
|
+
snapshot22[index] = child.value;
|
|
1038
1079
|
}
|
|
1039
|
-
return { valid: true, value:
|
|
1080
|
+
return { valid: true, value: snapshot22 };
|
|
1040
1081
|
}
|
|
1041
|
-
const
|
|
1082
|
+
const snapshot2 = /* @__PURE__ */ Object.create(null);
|
|
1042
1083
|
for (const key of Object.getOwnPropertyNames(value)) {
|
|
1043
1084
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1044
1085
|
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
@@ -1046,14 +1087,14 @@ function snapshotOwnData(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1046
1087
|
}
|
|
1047
1088
|
const child = snapshotOwnData(descriptor.value, ancestors);
|
|
1048
1089
|
if (!child.valid) return child;
|
|
1049
|
-
Object.defineProperty(
|
|
1090
|
+
Object.defineProperty(snapshot2, key, {
|
|
1050
1091
|
configurable: true,
|
|
1051
1092
|
enumerable: true,
|
|
1052
1093
|
value: child.value,
|
|
1053
1094
|
writable: true
|
|
1054
1095
|
});
|
|
1055
1096
|
}
|
|
1056
|
-
return { valid: true, value:
|
|
1097
|
+
return { valid: true, value: snapshot2 };
|
|
1057
1098
|
} catch {
|
|
1058
1099
|
return { valid: false };
|
|
1059
1100
|
} finally {
|
|
@@ -1070,12 +1111,12 @@ var ownArrayEntriesError = {
|
|
|
1070
1111
|
};
|
|
1071
1112
|
var guardedProjectValidator = Object.assign(
|
|
1072
1113
|
(value) => {
|
|
1073
|
-
const
|
|
1074
|
-
if (!
|
|
1114
|
+
const snapshot2 = snapshotOwnData(value);
|
|
1115
|
+
if (!snapshot2.valid) {
|
|
1075
1116
|
guardedProjectValidator.errors = [ownArrayEntriesError];
|
|
1076
1117
|
return false;
|
|
1077
1118
|
}
|
|
1078
|
-
const valid = compiledProjectValidator(
|
|
1119
|
+
const valid = compiledProjectValidator(snapshot2.value);
|
|
1079
1120
|
guardedProjectValidator.errors = compiledProjectValidator.errors ?? null;
|
|
1080
1121
|
return valid;
|
|
1081
1122
|
},
|
|
@@ -1621,8 +1662,8 @@ function validateRedirectDestination(destination, urlRegex) {
|
|
|
1621
1662
|
return issues;
|
|
1622
1663
|
}
|
|
1623
1664
|
function validateProjectDetailed(value) {
|
|
1624
|
-
const
|
|
1625
|
-
if (!
|
|
1665
|
+
const snapshot2 = snapshotOwnData(value);
|
|
1666
|
+
if (!snapshot2.valid) {
|
|
1626
1667
|
return {
|
|
1627
1668
|
valid: false,
|
|
1628
1669
|
errors: [
|
|
@@ -1635,10 +1676,10 @@ function validateProjectDetailed(value) {
|
|
|
1635
1676
|
]
|
|
1636
1677
|
};
|
|
1637
1678
|
}
|
|
1638
|
-
if (!projectValidator(
|
|
1679
|
+
if (!projectValidator(snapshot2.value)) {
|
|
1639
1680
|
return { valid: false, errors: ajvIssues(projectValidator.errors) };
|
|
1640
1681
|
}
|
|
1641
|
-
const errors = semanticIssues(
|
|
1682
|
+
const errors = semanticIssues(snapshot2.value);
|
|
1642
1683
|
return errors.length > 0 ? { valid: false, errors } : { valid: true, data: value };
|
|
1643
1684
|
}
|
|
1644
1685
|
|
|
@@ -1777,7 +1818,7 @@ function snapshotOwnData2(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1777
1818
|
return { valid: false };
|
|
1778
1819
|
}
|
|
1779
1820
|
}
|
|
1780
|
-
const
|
|
1821
|
+
const snapshot22 = new Array(length);
|
|
1781
1822
|
for (let index = 0; index < length; index += 1) {
|
|
1782
1823
|
const descriptor = Object.getOwnPropertyDescriptor(
|
|
1783
1824
|
value,
|
|
@@ -1788,11 +1829,11 @@ function snapshotOwnData2(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1788
1829
|
}
|
|
1789
1830
|
const child = snapshotOwnData2(descriptor.value, ancestors);
|
|
1790
1831
|
if (!child.valid) return child;
|
|
1791
|
-
|
|
1832
|
+
snapshot22[index] = child.value;
|
|
1792
1833
|
}
|
|
1793
|
-
return { valid: true, value:
|
|
1834
|
+
return { valid: true, value: snapshot22 };
|
|
1794
1835
|
}
|
|
1795
|
-
const
|
|
1836
|
+
const snapshot2 = /* @__PURE__ */ Object.create(null);
|
|
1796
1837
|
for (const key of Object.getOwnPropertyNames(value)) {
|
|
1797
1838
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1798
1839
|
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
@@ -1800,14 +1841,14 @@ function snapshotOwnData2(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
1800
1841
|
}
|
|
1801
1842
|
const child = snapshotOwnData2(descriptor.value, ancestors);
|
|
1802
1843
|
if (!child.valid) return child;
|
|
1803
|
-
Object.defineProperty(
|
|
1844
|
+
Object.defineProperty(snapshot2, key, {
|
|
1804
1845
|
configurable: true,
|
|
1805
1846
|
enumerable: true,
|
|
1806
1847
|
value: child.value,
|
|
1807
1848
|
writable: true
|
|
1808
1849
|
});
|
|
1809
1850
|
}
|
|
1810
|
-
return { valid: true, value:
|
|
1851
|
+
return { valid: true, value: snapshot2 };
|
|
1811
1852
|
} catch {
|
|
1812
1853
|
return { valid: false };
|
|
1813
1854
|
} finally {
|
|
@@ -1925,8 +1966,8 @@ function compileOperations(project) {
|
|
|
1925
1966
|
return operations;
|
|
1926
1967
|
}
|
|
1927
1968
|
function compileProject(value) {
|
|
1928
|
-
const
|
|
1929
|
-
if (!
|
|
1969
|
+
const snapshot2 = snapshotOwnData2(value);
|
|
1970
|
+
if (!snapshot2.valid) {
|
|
1930
1971
|
return {
|
|
1931
1972
|
ok: false,
|
|
1932
1973
|
operations: [],
|
|
@@ -1942,7 +1983,7 @@ function compileProject(value) {
|
|
|
1942
1983
|
}
|
|
1943
1984
|
let validation;
|
|
1944
1985
|
try {
|
|
1945
|
-
validation = validateProjectDetailed(
|
|
1986
|
+
validation = validateProjectDetailed(snapshot2.value);
|
|
1946
1987
|
} catch {
|
|
1947
1988
|
return { ok: false, operations: [], diagnostics: [invariantDiagnostic()] };
|
|
1948
1989
|
}
|
|
@@ -21694,7 +21735,7 @@ function snapshotOwnData3(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
21694
21735
|
return { valid: false };
|
|
21695
21736
|
}
|
|
21696
21737
|
}
|
|
21697
|
-
const
|
|
21738
|
+
const snapshot22 = new Array(length);
|
|
21698
21739
|
for (let index = 0; index < length; index += 1) {
|
|
21699
21740
|
const descriptor = Object.getOwnPropertyDescriptor(
|
|
21700
21741
|
value,
|
|
@@ -21705,13 +21746,13 @@ function snapshotOwnData3(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
21705
21746
|
}
|
|
21706
21747
|
const child = snapshotOwnData3(descriptor.value, ancestors);
|
|
21707
21748
|
if (!child.valid) return child;
|
|
21708
|
-
|
|
21749
|
+
snapshot22[index] = child.value;
|
|
21709
21750
|
}
|
|
21710
|
-
return { valid: true, value:
|
|
21751
|
+
return { valid: true, value: snapshot22 };
|
|
21711
21752
|
}
|
|
21712
21753
|
const names = Object.getOwnPropertyNames(value);
|
|
21713
21754
|
if (names.length > MAX_OBJECT_PROPERTIES) return { valid: false };
|
|
21714
|
-
const
|
|
21755
|
+
const snapshot2 = /* @__PURE__ */ Object.create(null);
|
|
21715
21756
|
for (const key of names) {
|
|
21716
21757
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
21717
21758
|
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
@@ -21719,14 +21760,14 @@ function snapshotOwnData3(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
|
21719
21760
|
}
|
|
21720
21761
|
const child = snapshotOwnData3(descriptor.value, ancestors);
|
|
21721
21762
|
if (!child.valid) return child;
|
|
21722
|
-
Object.defineProperty(
|
|
21763
|
+
Object.defineProperty(snapshot2, key, {
|
|
21723
21764
|
configurable: true,
|
|
21724
21765
|
enumerable: true,
|
|
21725
21766
|
value: child.value,
|
|
21726
21767
|
writable: true
|
|
21727
21768
|
});
|
|
21728
21769
|
}
|
|
21729
|
-
return { valid: true, value:
|
|
21770
|
+
return { valid: true, value: snapshot2 };
|
|
21730
21771
|
} catch {
|
|
21731
21772
|
return { valid: false };
|
|
21732
21773
|
} finally {
|
|
@@ -21808,10 +21849,10 @@ function validId(value) {
|
|
|
21808
21849
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(value);
|
|
21809
21850
|
}
|
|
21810
21851
|
function exactDescriptor(value) {
|
|
21811
|
-
const
|
|
21812
|
-
if (!
|
|
21852
|
+
const snapshot2 = snapshotOwnData3(value);
|
|
21853
|
+
if (!snapshot2.valid || snapshot2.value === null || typeof snapshot2.value !== "object" || Array.isArray(snapshot2.value))
|
|
21813
21854
|
return null;
|
|
21814
|
-
const record =
|
|
21855
|
+
const record = snapshot2.value;
|
|
21815
21856
|
const keys = ["groupId", "ruleId", "operationId", "kind", "target", "method"];
|
|
21816
21857
|
if (Object.keys(record).length !== keys.length || !keys.every((key) => hasOwn(record, key)))
|
|
21817
21858
|
return null;
|
|
@@ -22653,7 +22694,7 @@ function readBoundedBody(req, maxBytes) {
|
|
|
22653
22694
|
const chunks = [];
|
|
22654
22695
|
let total = 0;
|
|
22655
22696
|
let settled = false;
|
|
22656
|
-
const
|
|
22697
|
+
const finish2 = (result) => {
|
|
22657
22698
|
if (settled) return;
|
|
22658
22699
|
settled = true;
|
|
22659
22700
|
resolve22(result);
|
|
@@ -22662,16 +22703,16 @@ function readBoundedBody(req, maxBytes) {
|
|
|
22662
22703
|
total += chunk.byteLength;
|
|
22663
22704
|
if (total > maxBytes) {
|
|
22664
22705
|
req.destroy();
|
|
22665
|
-
|
|
22706
|
+
finish2({ ok: false, reason: "oversized" });
|
|
22666
22707
|
return;
|
|
22667
22708
|
}
|
|
22668
22709
|
chunks.push(chunk);
|
|
22669
22710
|
});
|
|
22670
22711
|
req.on("end", () => {
|
|
22671
|
-
|
|
22712
|
+
finish2({ ok: true, body: Buffer.concat(chunks) });
|
|
22672
22713
|
});
|
|
22673
22714
|
req.on("error", () => {
|
|
22674
|
-
|
|
22715
|
+
finish2({ ok: true, body: Buffer.concat(chunks) });
|
|
22675
22716
|
});
|
|
22676
22717
|
});
|
|
22677
22718
|
}
|
|
@@ -23841,12 +23882,12 @@ function makeGrant(value, matcherById) {
|
|
|
23841
23882
|
});
|
|
23842
23883
|
}
|
|
23843
23884
|
function normalizeRuntimePreset(value) {
|
|
23844
|
-
const
|
|
23845
|
-
if (!
|
|
23885
|
+
const snapshot2 = snapshotOwnData3(value);
|
|
23886
|
+
if (!snapshot2.valid || snapshot2.value === null || typeof snapshot2.value !== "object" || Array.isArray(snapshot2.value)) {
|
|
23846
23887
|
console.error("[rogatio-host] preset: snapshot invalid");
|
|
23847
23888
|
return failure("runtime.invalid-preset");
|
|
23848
23889
|
}
|
|
23849
|
-
const record =
|
|
23890
|
+
const record = snapshot2.value;
|
|
23850
23891
|
const allowedKeys = ["version", "limits", "matchers", "grants", "mocks"];
|
|
23851
23892
|
const requiredKeys = ["version", "limits", "matchers", "grants"];
|
|
23852
23893
|
const keyCount = Object.keys(record).length;
|
|
@@ -25754,6 +25795,30 @@ Exit codes:
|
|
|
25754
25795
|
1 Validation/compile/test errors
|
|
25755
25796
|
2 Usage error (invalid arguments, missing input)`);
|
|
25756
25797
|
}
|
|
25798
|
+
function showImportHelp() {
|
|
25799
|
+
console.log(`Usage: rogatio import requestly <export.json> [options]
|
|
25800
|
+
|
|
25801
|
+
Migrate a Requestly rule export into a Rogatio project file.
|
|
25802
|
+
|
|
25803
|
+
Arguments:
|
|
25804
|
+
export.json Requestly export (a JSON array of rules and groups, a single
|
|
25805
|
+
rule, or an object with a rules array)
|
|
25806
|
+
|
|
25807
|
+
Options:
|
|
25808
|
+
--out <path> Project file to write (default: .rogatio.json in the current directory)
|
|
25809
|
+
--merge Append imported groups onto an existing version-2 project
|
|
25810
|
+
--json Print the import report as JSON
|
|
25811
|
+
--help, -h Show this help
|
|
25812
|
+
|
|
25813
|
+
The command validates the mapped project with the schema and compiler before
|
|
25814
|
+
writing. An existing --out file is left untouched unless --merge is set.
|
|
25815
|
+
Skipped and changed rules are listed; nothing is dropped silently.
|
|
25816
|
+
|
|
25817
|
+
Exit codes:
|
|
25818
|
+
0 The project was written (skipped rules do not fail the command)
|
|
25819
|
+
1 The mapped or merged project failed validation. Nothing is written.
|
|
25820
|
+
2 Usage, unreadable input, an export that is not Requestly data, or a refusal to overwrite`);
|
|
25821
|
+
}
|
|
25757
25822
|
function showAIHelp() {
|
|
25758
25823
|
console.log(`Usage: rogatio ai <command> [options]
|
|
25759
25824
|
|
|
@@ -25773,13 +25838,13 @@ Options:
|
|
|
25773
25838
|
// packages/cli/src/commands/ai.ts
|
|
25774
25839
|
var pendingInput = "";
|
|
25775
25840
|
function readLine() {
|
|
25776
|
-
return new Promise((
|
|
25841
|
+
return new Promise((resolve10) => {
|
|
25777
25842
|
const take = () => {
|
|
25778
25843
|
const newline = pendingInput.search(/[\r\n]/u);
|
|
25779
25844
|
if (newline === -1) return false;
|
|
25780
25845
|
const line = pendingInput.slice(0, newline);
|
|
25781
25846
|
pendingInput = pendingInput.slice(newline + 1);
|
|
25782
|
-
|
|
25847
|
+
resolve10(line);
|
|
25783
25848
|
return true;
|
|
25784
25849
|
};
|
|
25785
25850
|
if (take()) return;
|
|
@@ -25801,14 +25866,14 @@ async function promptSecret(prompt) {
|
|
|
25801
25866
|
process.stdout.write(prompt);
|
|
25802
25867
|
process.stdin.setRawMode(true);
|
|
25803
25868
|
try {
|
|
25804
|
-
return await new Promise((
|
|
25869
|
+
return await new Promise((resolve10) => {
|
|
25805
25870
|
let input = "";
|
|
25806
25871
|
let settled = false;
|
|
25807
|
-
const
|
|
25872
|
+
const finish2 = () => {
|
|
25808
25873
|
settled = true;
|
|
25809
25874
|
process.stdin.off("data", onData);
|
|
25810
25875
|
process.stdin.pause();
|
|
25811
|
-
|
|
25876
|
+
resolve10(input.trim());
|
|
25812
25877
|
};
|
|
25813
25878
|
const consume = (text) => {
|
|
25814
25879
|
if (settled) return;
|
|
@@ -25817,7 +25882,7 @@ async function promptSecret(prompt) {
|
|
|
25817
25882
|
if (char === "\n" || char === "\r") {
|
|
25818
25883
|
process.stdout.write("\n");
|
|
25819
25884
|
pendingInput = text.slice(index + 1);
|
|
25820
|
-
|
|
25885
|
+
finish2();
|
|
25821
25886
|
return;
|
|
25822
25887
|
}
|
|
25823
25888
|
if (char === "") {
|
|
@@ -26011,11 +26076,11 @@ function createServer3(handler, options = {}) {
|
|
|
26011
26076
|
let port = null;
|
|
26012
26077
|
let started = false;
|
|
26013
26078
|
async function listenOn(candidatePort) {
|
|
26014
|
-
await new Promise((
|
|
26079
|
+
await new Promise((resolve10, reject) => {
|
|
26015
26080
|
server.once("error", reject);
|
|
26016
26081
|
server.listen(candidatePort, "127.0.0.1", () => {
|
|
26017
26082
|
server.off("error", reject);
|
|
26018
|
-
|
|
26083
|
+
resolve10();
|
|
26019
26084
|
});
|
|
26020
26085
|
});
|
|
26021
26086
|
}
|
|
@@ -26066,8 +26131,8 @@ function createServer3(handler, options = {}) {
|
|
|
26066
26131
|
},
|
|
26067
26132
|
async stop() {
|
|
26068
26133
|
if (!started) return;
|
|
26069
|
-
await new Promise((
|
|
26070
|
-
server.close(() =>
|
|
26134
|
+
await new Promise((resolve10) => {
|
|
26135
|
+
server.close(() => resolve10());
|
|
26071
26136
|
});
|
|
26072
26137
|
started = false;
|
|
26073
26138
|
}
|
|
@@ -26386,7 +26451,7 @@ var RequestBodyError = class extends Error {
|
|
|
26386
26451
|
code = "request-body-too-large";
|
|
26387
26452
|
};
|
|
26388
26453
|
function getRequestBody(req) {
|
|
26389
|
-
return new Promise((
|
|
26454
|
+
return new Promise((resolve10, reject) => {
|
|
26390
26455
|
let body = "";
|
|
26391
26456
|
let bytes = 0;
|
|
26392
26457
|
let settled = false;
|
|
@@ -26416,7 +26481,7 @@ function getRequestBody(req) {
|
|
|
26416
26481
|
req.on("end", () => {
|
|
26417
26482
|
if (!settled) {
|
|
26418
26483
|
settled = true;
|
|
26419
|
-
|
|
26484
|
+
resolve10(body);
|
|
26420
26485
|
}
|
|
26421
26486
|
});
|
|
26422
26487
|
req.on("error", (error) => {
|
|
@@ -27031,7 +27096,7 @@ async function launchBrowser(url) {
|
|
|
27031
27096
|
`Unsupported platform: ${platform}`
|
|
27032
27097
|
);
|
|
27033
27098
|
}
|
|
27034
|
-
return new Promise((
|
|
27099
|
+
return new Promise((resolve10) => {
|
|
27035
27100
|
const child = spawn4(command, args, {
|
|
27036
27101
|
detached: true,
|
|
27037
27102
|
stdio: "ignore"
|
|
@@ -27039,13 +27104,13 @@ async function launchBrowser(url) {
|
|
|
27039
27104
|
child.unref();
|
|
27040
27105
|
child.on("error", (err) => {
|
|
27041
27106
|
if (err.code === "ENOENT") {
|
|
27042
|
-
|
|
27107
|
+
resolve10(false);
|
|
27043
27108
|
} else {
|
|
27044
|
-
|
|
27109
|
+
resolve10(false);
|
|
27045
27110
|
}
|
|
27046
27111
|
});
|
|
27047
27112
|
child.on("close", (code) => {
|
|
27048
|
-
|
|
27113
|
+
resolve10(code === 0);
|
|
27049
27114
|
});
|
|
27050
27115
|
});
|
|
27051
27116
|
}
|
|
@@ -27374,6 +27439,17 @@ function createJsonFileProjectStorage() {
|
|
|
27374
27439
|
|
|
27375
27440
|
// packages/cli/src/utils/file.ts
|
|
27376
27441
|
var defaultStorage = createJsonFileProjectStorage();
|
|
27442
|
+
async function writeProject(path2, data) {
|
|
27443
|
+
try {
|
|
27444
|
+
await defaultStorage.update(path2, data);
|
|
27445
|
+
} catch (e) {
|
|
27446
|
+
if (e instanceof ProjectStorageError && e.code === "not-found") {
|
|
27447
|
+
await defaultStorage.create({ id: path2, data });
|
|
27448
|
+
return;
|
|
27449
|
+
}
|
|
27450
|
+
throw e;
|
|
27451
|
+
}
|
|
27452
|
+
}
|
|
27377
27453
|
|
|
27378
27454
|
// packages/cli/src/commands/edit.ts
|
|
27379
27455
|
async function editCommand(args, options = {}) {
|
|
@@ -27527,11 +27603,11 @@ async function editCommand(args, options = {}) {
|
|
|
27527
27603
|
} else {
|
|
27528
27604
|
console.log(`Editor available at: ${editorUrl}`);
|
|
27529
27605
|
}
|
|
27530
|
-
const exitCodePromise = new Promise((
|
|
27606
|
+
const exitCodePromise = new Promise((resolve10) => {
|
|
27531
27607
|
const checkShutdown = setInterval(() => {
|
|
27532
27608
|
if (shutdownCalled) {
|
|
27533
27609
|
clearInterval(checkShutdown);
|
|
27534
|
-
|
|
27610
|
+
resolve10(0);
|
|
27535
27611
|
}
|
|
27536
27612
|
}, 100);
|
|
27537
27613
|
const handleSignal = () => {
|
|
@@ -27539,8 +27615,8 @@ async function editCommand(args, options = {}) {
|
|
|
27539
27615
|
};
|
|
27540
27616
|
process.on("SIGINT", handleSignal);
|
|
27541
27617
|
process.on("SIGTERM", handleSignal);
|
|
27542
|
-
const originalResolve =
|
|
27543
|
-
|
|
27618
|
+
const originalResolve = resolve10;
|
|
27619
|
+
resolve10 = (code) => {
|
|
27544
27620
|
clearInterval(checkShutdown);
|
|
27545
27621
|
process.off("SIGINT", handleSignal);
|
|
27546
27622
|
process.off("SIGTERM", handleSignal);
|
|
@@ -27763,200 +27839,2241 @@ function generateEditorHtml(apiBase, csrfToken, filePath, aiConfigured) {
|
|
|
27763
27839
|
</html>`;
|
|
27764
27840
|
}
|
|
27765
27841
|
|
|
27766
|
-
// packages/cli/src/commands/
|
|
27767
|
-
import {
|
|
27768
|
-
import {
|
|
27769
|
-
|
|
27770
|
-
|
|
27771
|
-
|
|
27772
|
-
|
|
27773
|
-
|
|
27774
|
-
|
|
27775
|
-
|
|
27776
|
-
|
|
27777
|
-
|
|
27778
|
-
|
|
27779
|
-
|
|
27780
|
-
|
|
27781
|
-
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27794
|
-
|
|
27795
|
-
|
|
27796
|
-
|
|
27797
|
-
|
|
27798
|
-
|
|
27799
|
-
|
|
27800
|
-
|
|
27801
|
-
|
|
27802
|
-
|
|
27803
|
-
|
|
27804
|
-
|
|
27805
|
-
|
|
27806
|
-
const
|
|
27807
|
-
|
|
27808
|
-
|
|
27809
|
-
|
|
27810
|
-
|
|
27811
|
-
|
|
27812
|
-
|
|
27813
|
-
|
|
27814
|
-
|
|
27815
|
-
|
|
27816
|
-
|
|
27817
|
-
|
|
27818
|
-
}
|
|
27819
|
-
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
return
|
|
27842
|
+
// packages/cli/src/commands/import.ts
|
|
27843
|
+
import { access as access2, readFile as readFile6 } from "node:fs/promises";
|
|
27844
|
+
import { resolve as resolve5 } from "node:path";
|
|
27845
|
+
|
|
27846
|
+
// packages/requestly-import/dist/node/index.js
|
|
27847
|
+
function isRecord3(value) {
|
|
27848
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
27849
|
+
}
|
|
27850
|
+
function ownString3(record, key) {
|
|
27851
|
+
if (!Object.hasOwn(record, key)) return void 0;
|
|
27852
|
+
const value = record[key];
|
|
27853
|
+
return typeof value === "string" ? value : void 0;
|
|
27854
|
+
}
|
|
27855
|
+
function ownBoolean(record, key) {
|
|
27856
|
+
if (!Object.hasOwn(record, key)) return void 0;
|
|
27857
|
+
const value = record[key];
|
|
27858
|
+
return typeof value === "boolean" ? value : void 0;
|
|
27859
|
+
}
|
|
27860
|
+
function displayName(name) {
|
|
27861
|
+
const collapsed = name.replace(/\s+/g, " ").trim();
|
|
27862
|
+
return collapsed.length > 0 ? collapsed : "(unnamed)";
|
|
27863
|
+
}
|
|
27864
|
+
function cleanLabel(name, fallback) {
|
|
27865
|
+
const collapsed = name.replace(/\s+/g, " ").trim();
|
|
27866
|
+
return collapsed.length > 0 ? collapsed : fallback;
|
|
27867
|
+
}
|
|
27868
|
+
var REQUESTLY_REGEX = /^\/(.+)\/(|i|g|ig|gi)$/;
|
|
27869
|
+
var LITERAL_HOSTNAME = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
|
27870
|
+
var RESOURCE_SET = new Set(RESOURCE_TYPES);
|
|
27871
|
+
var METHOD_SET = new Set(HTTP_METHODS);
|
|
27872
|
+
var SOURCE_CHANGE = {
|
|
27873
|
+
caseInsensitive: "The case-insensitive match flag was dropped. Rogatio regular expressions are case-sensitive.",
|
|
27874
|
+
hostPort: "Requestly compares the host and a non-default port. This host condition matches the hostname on any port.",
|
|
27875
|
+
hostAsUrl: "The host condition was imported as a URL regular expression so the port stays part of the match.",
|
|
27876
|
+
path: "The path condition was imported as a URL regular expression."
|
|
27877
|
+
};
|
|
27878
|
+
function escapeRegex(literal) {
|
|
27879
|
+
return literal.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
27880
|
+
}
|
|
27881
|
+
function convertSource(source) {
|
|
27882
|
+
const key = ownString3(source, "key");
|
|
27883
|
+
const operator = ownString3(source, "operator");
|
|
27884
|
+
const value = ownString3(source, "value");
|
|
27885
|
+
if (key === void 0 || operator === void 0 || value === void 0) {
|
|
27886
|
+
return { ok: false, reason: "The source condition is missing." };
|
|
27887
|
+
}
|
|
27888
|
+
if (operator === "Equals" && value.length === 0) {
|
|
27889
|
+
return { ok: false, reason: "The source value is empty." };
|
|
27890
|
+
}
|
|
27891
|
+
if (key === "Url") return convertUrl(operator, value);
|
|
27892
|
+
if (key === "host") return convertHost(operator, value);
|
|
27893
|
+
if (key === "path") return convertPath(operator, value);
|
|
27894
|
+
return { ok: false, reason: `Unsupported source key "${key}".` };
|
|
27895
|
+
}
|
|
27896
|
+
function readFilters(source) {
|
|
27897
|
+
if (!Object.hasOwn(source, "filters") || source.filters === void 0) {
|
|
27898
|
+
return {
|
|
27899
|
+
ok: true,
|
|
27900
|
+
filters: {
|
|
27901
|
+
resourceTypes: [...RESOURCE_TYPES],
|
|
27902
|
+
methods: "all",
|
|
27903
|
+
changes: []
|
|
27904
|
+
}
|
|
27905
|
+
};
|
|
27823
27906
|
}
|
|
27824
|
-
|
|
27825
|
-
|
|
27826
|
-
|
|
27827
|
-
|
|
27828
|
-
|
|
27829
|
-
|
|
27830
|
-
|
|
27831
|
-
|
|
27832
|
-
|
|
27907
|
+
const raw = source.filters;
|
|
27908
|
+
const list = Array.isArray(raw) ? raw : [raw];
|
|
27909
|
+
const changes = [];
|
|
27910
|
+
let resourceTypes;
|
|
27911
|
+
let methods;
|
|
27912
|
+
for (const entry of list) {
|
|
27913
|
+
if (!isRecord3(entry)) {
|
|
27914
|
+
return { ok: false, reason: "A source filter is not an object." };
|
|
27915
|
+
}
|
|
27916
|
+
if (isActive(entry.pageUrl) || isActive(entry.pageDomains)) {
|
|
27917
|
+
return {
|
|
27918
|
+
ok: false,
|
|
27919
|
+
reason: "Page URL and page domain filters are not supported, so the rule was not imported."
|
|
27920
|
+
};
|
|
27921
|
+
}
|
|
27922
|
+
if (isActive(entry.requestPayload)) {
|
|
27923
|
+
return {
|
|
27924
|
+
ok: false,
|
|
27925
|
+
reason: "Request payload filters are not supported, so the rule was not imported."
|
|
27926
|
+
};
|
|
27927
|
+
}
|
|
27928
|
+
if (Object.hasOwn(entry, "resourceType")) {
|
|
27929
|
+
const read = readResourceTypes(entry.resourceType);
|
|
27930
|
+
if (!read.ok) return read;
|
|
27931
|
+
resourceTypes = intersect(resourceTypes, read.values);
|
|
27932
|
+
changes.push(...read.changes);
|
|
27933
|
+
}
|
|
27934
|
+
if (Object.hasOwn(entry, "requestMethod")) {
|
|
27935
|
+
const read = readMethods(entry.requestMethod);
|
|
27936
|
+
if (!read.ok) return read;
|
|
27937
|
+
methods = intersect(methods, read.values);
|
|
27938
|
+
changes.push(...read.changes);
|
|
27939
|
+
}
|
|
27940
|
+
}
|
|
27941
|
+
if (resourceTypes !== void 0 && resourceTypes.length === 0) {
|
|
27942
|
+
return {
|
|
27943
|
+
ok: false,
|
|
27944
|
+
reason: "The resource type filter matches no supported type."
|
|
27833
27945
|
};
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27838
|
-
|
|
27946
|
+
}
|
|
27947
|
+
if (methods !== void 0 && methods.length === 0) {
|
|
27948
|
+
return {
|
|
27949
|
+
ok: false,
|
|
27950
|
+
reason: "The request method filter matches no supported method."
|
|
27951
|
+
};
|
|
27952
|
+
}
|
|
27953
|
+
return {
|
|
27954
|
+
ok: true,
|
|
27955
|
+
filters: {
|
|
27956
|
+
resourceTypes: resourceTypes ?? [...RESOURCE_TYPES],
|
|
27957
|
+
methods: methods ?? "all",
|
|
27958
|
+
changes
|
|
27839
27959
|
}
|
|
27840
|
-
|
|
27960
|
+
};
|
|
27961
|
+
}
|
|
27962
|
+
function acceptRegex(pattern) {
|
|
27963
|
+
if (pattern.length === 0 || pattern.length > LIMITS.maxUrlRegexLength) {
|
|
27964
|
+
return {
|
|
27965
|
+
ok: false,
|
|
27966
|
+
reason: "The source regular expression exceeds the length limit."
|
|
27967
|
+
};
|
|
27841
27968
|
}
|
|
27842
|
-
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
return
|
|
27969
|
+
if (compileUrlRegex(pattern) === null) {
|
|
27970
|
+
return { ok: false, reason: "The source regular expression is invalid." };
|
|
27971
|
+
}
|
|
27972
|
+
return { ok: true };
|
|
27846
27973
|
}
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
if (subcommand === "install") {
|
|
27851
|
-
const extIdIndex = args.indexOf("--extension-id");
|
|
27852
|
-
if (extIdIndex === -1 || extIdIndex + 1 >= args.length) {
|
|
27853
|
-
console.error("Error: --extension-id is required for install command");
|
|
27854
|
-
showRuntimeHelp();
|
|
27855
|
-
return 2;
|
|
27856
|
-
}
|
|
27857
|
-
extensionId = args[extIdIndex + 1];
|
|
27858
|
-
if (!/^[a-p]{32}$/.test(extensionId)) {
|
|
27859
|
-
console.error(
|
|
27860
|
-
"Error: --extension-id must be exactly 32 lowercase characters from a through p"
|
|
27861
|
-
);
|
|
27862
|
-
return 2;
|
|
27863
|
-
}
|
|
27974
|
+
function convertUrl(operator, value) {
|
|
27975
|
+
if (operator === "Equals") {
|
|
27976
|
+
return finishUrl(`^${escapeRegex(value)}$`, []);
|
|
27864
27977
|
}
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
|
|
27871
|
-
|
|
27872
|
-
|
|
27873
|
-
|
|
27874
|
-
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27883
|
-
|
|
27978
|
+
if (operator === "Contains") {
|
|
27979
|
+
const pattern = value.length === 0 ? "[\\s\\S]*" : escapeRegex(value);
|
|
27980
|
+
return finishUrl(pattern, []);
|
|
27981
|
+
}
|
|
27982
|
+
if (operator === "Wildcard_Matches") {
|
|
27983
|
+
return finishUrl(`^${wildcardBody(value)}$`, []);
|
|
27984
|
+
}
|
|
27985
|
+
if (operator === "Matches") return finishMatches(value);
|
|
27986
|
+
return { ok: false, reason: `Unsupported source operator "${operator}".` };
|
|
27987
|
+
}
|
|
27988
|
+
function convertHost(operator, value) {
|
|
27989
|
+
if (value.includes(":") && splitHostPort(value) === null) {
|
|
27990
|
+
return { ok: false, reason: "IPv6 host conditions are not supported." };
|
|
27991
|
+
}
|
|
27992
|
+
if (operator === "Equals") {
|
|
27993
|
+
if (isLiteralHostname(value)) {
|
|
27994
|
+
return finish(
|
|
27995
|
+
{ key: "host", operator: "regex", value: `^${escapeRegex(value)}$` },
|
|
27996
|
+
[SOURCE_CHANGE.hostPort]
|
|
27884
27997
|
);
|
|
27885
27998
|
}
|
|
27886
|
-
|
|
27887
|
-
|
|
27888
|
-
|
|
27889
|
-
|
|
27890
|
-
|
|
27891
|
-
}
|
|
27892
|
-
try {
|
|
27893
|
-
await rm2(join7(installRoot, "runtime-host.log"), { force: true });
|
|
27894
|
-
} catch {
|
|
27895
|
-
}
|
|
27896
|
-
return reportTrust(
|
|
27897
|
-
"uninstall",
|
|
27898
|
-
uninstallResult,
|
|
27899
|
-
"runtime uninstall complete: manifest + device-local CA removed"
|
|
27900
|
-
);
|
|
27999
|
+
const port = splitHostPort(value);
|
|
28000
|
+
if (port !== null) {
|
|
28001
|
+
return finishUrl(`^https?:\\/\\/${escapeRegex(value)}(?:[/?#].*)?$`, [
|
|
28002
|
+
SOURCE_CHANGE.hostAsUrl
|
|
28003
|
+
]);
|
|
27901
28004
|
}
|
|
27902
|
-
|
|
27903
|
-
|
|
27904
|
-
|
|
27905
|
-
|
|
27906
|
-
|
|
27907
|
-
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27925
|
-
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
|
|
27929
|
-
|
|
27930
|
-
|
|
27931
|
-
|
|
27932
|
-
|
|
27933
|
-
);
|
|
27934
|
-
if (reason === "no-allowed-origins")
|
|
27935
|
-
console.error(
|
|
27936
|
-
" Hint: re-run 'rogatio runtime install --extension-id <id>' to pin your extension ID"
|
|
27937
|
-
);
|
|
27938
|
-
if (reason === "ca-not-trusted")
|
|
27939
|
-
console.error(
|
|
27940
|
-
" Hint: re-run 'rogatio runtime install --extension-id <id>' with elevated privileges to trust the CA"
|
|
27941
|
-
);
|
|
27942
|
-
}
|
|
27943
|
-
return 1;
|
|
28005
|
+
return {
|
|
28006
|
+
ok: false,
|
|
28007
|
+
reason: "The host value is not a literal hostname or hostname:port."
|
|
28008
|
+
};
|
|
28009
|
+
}
|
|
28010
|
+
if (operator === "Contains") {
|
|
28011
|
+
const body = value.length === 0 ? "[^/?#]*" : `[^/?#]*${escapeRegex(value)}[^/?#]*`;
|
|
28012
|
+
return finishUrl(`^https?:\\/\\/${body}(?:[/?#].*)?$`, [
|
|
28013
|
+
SOURCE_CHANGE.hostAsUrl
|
|
28014
|
+
]);
|
|
28015
|
+
}
|
|
28016
|
+
if (operator === "Wildcard_Matches") {
|
|
28017
|
+
return finishUrl(`^https?:\\/\\/${wildcardBody(value)}(?:[/?#].*)?$`, [
|
|
28018
|
+
SOURCE_CHANGE.hostAsUrl
|
|
28019
|
+
]);
|
|
28020
|
+
}
|
|
28021
|
+
if (operator === "Matches") {
|
|
28022
|
+
const parsed = parseRequestlyRegex(value);
|
|
28023
|
+
if (!parsed.ok) return parsed;
|
|
28024
|
+
return finishUrl(
|
|
28025
|
+
`^https?:\\/\\/(?:${parsed.source})(?:[/?#].*)?$`,
|
|
28026
|
+
parsed.caseInsensitive ? [SOURCE_CHANGE.hostAsUrl, SOURCE_CHANGE.caseInsensitive] : [SOURCE_CHANGE.hostAsUrl]
|
|
28027
|
+
);
|
|
28028
|
+
}
|
|
28029
|
+
return { ok: false, reason: `Unsupported source operator "${operator}".` };
|
|
28030
|
+
}
|
|
28031
|
+
function convertPath(operator, value) {
|
|
28032
|
+
const changes = [SOURCE_CHANGE.path];
|
|
28033
|
+
if (operator === "Equals") {
|
|
28034
|
+
if (value === "/" || value.length === 0) {
|
|
28035
|
+
return finishUrl("^https?:\\/\\/[^/?#]+\\/?(?:[?#].*)?$", changes);
|
|
27944
28036
|
}
|
|
27945
|
-
|
|
27946
|
-
|
|
27947
|
-
|
|
27948
|
-
|
|
28037
|
+
return finishUrl(
|
|
28038
|
+
`^https?:\\/\\/[^/?#]+${escapeRegex(value)}(?:[?#].*)?$`,
|
|
28039
|
+
changes
|
|
28040
|
+
);
|
|
28041
|
+
}
|
|
28042
|
+
if (operator === "Contains") {
|
|
28043
|
+
const body = value.length === 0 ? "[^?#]*" : `[^?#]*${escapeRegex(value)}[^?#]*`;
|
|
28044
|
+
return finishUrl(`^https?:\\/\\/[^/?#]+\\/${body}(?:[?#].*)?$`, changes);
|
|
27949
28045
|
}
|
|
28046
|
+
if (operator === "Wildcard_Matches") {
|
|
28047
|
+
return finishUrl(
|
|
28048
|
+
`^https?:\\/\\/[^/?#]+${wildcardBody(value)}(?:[?#].*)?$`,
|
|
28049
|
+
changes
|
|
28050
|
+
);
|
|
28051
|
+
}
|
|
28052
|
+
if (operator === "Matches") {
|
|
28053
|
+
const parsed = parseRequestlyRegex(value);
|
|
28054
|
+
if (!parsed.ok) return parsed;
|
|
28055
|
+
const inner = stripAnchors(parsed.source);
|
|
28056
|
+
if (parsed.caseInsensitive) changes.push(SOURCE_CHANGE.caseInsensitive);
|
|
28057
|
+
return finishUrl(`^https?:\\/\\/[^/?#]+(?:${inner})(?:[?#].*)?$`, changes);
|
|
28058
|
+
}
|
|
28059
|
+
return { ok: false, reason: `Unsupported source operator "${operator}".` };
|
|
28060
|
+
}
|
|
28061
|
+
function finishMatches(value) {
|
|
28062
|
+
const parsed = parseRequestlyRegex(value);
|
|
28063
|
+
if (!parsed.ok) return parsed;
|
|
28064
|
+
return finishUrl(
|
|
28065
|
+
parsed.source,
|
|
28066
|
+
parsed.caseInsensitive ? [SOURCE_CHANGE.caseInsensitive] : []
|
|
28067
|
+
);
|
|
27950
28068
|
}
|
|
27951
|
-
|
|
27952
|
-
|
|
27953
|
-
|
|
27954
|
-
|
|
27955
|
-
|
|
28069
|
+
function finishUrl(pattern, changes) {
|
|
28070
|
+
return finish({ key: "url", operator: "regex", value: pattern }, changes);
|
|
28071
|
+
}
|
|
28072
|
+
function finish(source, changes) {
|
|
28073
|
+
const accepted = acceptRegex(source.value);
|
|
28074
|
+
if (!accepted.ok) return accepted;
|
|
28075
|
+
return { ok: true, converted: { source, changes } };
|
|
28076
|
+
}
|
|
28077
|
+
function parseRequestlyRegex(value) {
|
|
28078
|
+
const match = REQUESTLY_REGEX.exec(value);
|
|
28079
|
+
const source = match?.[1];
|
|
28080
|
+
if (match === null || source === void 0) {
|
|
28081
|
+
return {
|
|
28082
|
+
ok: false,
|
|
28083
|
+
reason: "A Matches value must be a /pattern/flags regular expression with flags i, g, or both."
|
|
28084
|
+
};
|
|
28085
|
+
}
|
|
28086
|
+
const flags = match[2] ?? "";
|
|
28087
|
+
try {
|
|
28088
|
+
new RegExp(source);
|
|
28089
|
+
} catch {
|
|
28090
|
+
return { ok: false, reason: "The source regular expression is invalid." };
|
|
28091
|
+
}
|
|
28092
|
+
return { ok: true, source, caseInsensitive: flags.includes("i") };
|
|
28093
|
+
}
|
|
28094
|
+
function wildcardBody(pattern) {
|
|
28095
|
+
return pattern.split("*").map((part) => escapeRegex(part)).join("(.*?)");
|
|
28096
|
+
}
|
|
28097
|
+
function isRequestlyRegexLiteral(value) {
|
|
28098
|
+
return REQUESTLY_REGEX.test(value);
|
|
28099
|
+
}
|
|
28100
|
+
function stripAnchors(pattern) {
|
|
28101
|
+
let next = pattern;
|
|
28102
|
+
if (next.startsWith("^")) next = next.slice(1);
|
|
28103
|
+
if (next.endsWith("$")) next = next.slice(0, -1);
|
|
28104
|
+
return next;
|
|
28105
|
+
}
|
|
28106
|
+
function isLiteralHostname(value) {
|
|
28107
|
+
return LITERAL_HOSTNAME.test(value);
|
|
28108
|
+
}
|
|
28109
|
+
function splitHostPort(value) {
|
|
28110
|
+
const index = value.lastIndexOf(":");
|
|
28111
|
+
if (index <= 0) return null;
|
|
28112
|
+
const host = value.slice(0, index);
|
|
28113
|
+
const port = value.slice(index + 1);
|
|
28114
|
+
if (!/^[0-9]+$/.test(port) || host.includes(":") || !isLiteralHostname(host)) {
|
|
28115
|
+
return null;
|
|
28116
|
+
}
|
|
28117
|
+
return { host, port };
|
|
28118
|
+
}
|
|
28119
|
+
function isActive(value) {
|
|
28120
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
28121
|
+
if (isRecord3(value)) return Object.keys(value).length > 0;
|
|
28122
|
+
if (typeof value === "string") return value.length > 0;
|
|
28123
|
+
return value !== void 0 && value !== null;
|
|
28124
|
+
}
|
|
28125
|
+
function readResourceTypes(value) {
|
|
28126
|
+
if (!Array.isArray(value)) {
|
|
28127
|
+
return { ok: false, reason: "The resource type filter is not a list." };
|
|
28128
|
+
}
|
|
28129
|
+
if (value.length === 0) {
|
|
28130
|
+
return {
|
|
28131
|
+
ok: false,
|
|
28132
|
+
reason: "The resource type filter matches no supported type."
|
|
28133
|
+
};
|
|
28134
|
+
}
|
|
28135
|
+
const known = [];
|
|
28136
|
+
const unknown = [];
|
|
28137
|
+
for (const entry of value) {
|
|
28138
|
+
if (typeof entry !== "string") continue;
|
|
28139
|
+
if (RESOURCE_SET.has(entry)) known.push(entry);
|
|
28140
|
+
else unknown.push(entry);
|
|
28141
|
+
}
|
|
28142
|
+
const ordered = RESOURCE_TYPES.filter((type) => known.includes(type));
|
|
28143
|
+
const changes = unknown.length > 0 ? [`Dropped unrecognized resource types: ${unknown.join(", ")}.`] : [];
|
|
28144
|
+
return { ok: true, values: ordered, changes };
|
|
28145
|
+
}
|
|
28146
|
+
function readMethods(value) {
|
|
28147
|
+
if (!Array.isArray(value)) {
|
|
28148
|
+
return { ok: false, reason: "The request method filter is not a list." };
|
|
28149
|
+
}
|
|
28150
|
+
if (value.length === 0) {
|
|
28151
|
+
return {
|
|
28152
|
+
ok: false,
|
|
28153
|
+
reason: "The request method filter matches no supported method."
|
|
28154
|
+
};
|
|
28155
|
+
}
|
|
28156
|
+
const known = [];
|
|
28157
|
+
const unknown = [];
|
|
28158
|
+
for (const entry of value) {
|
|
28159
|
+
if (typeof entry !== "string") continue;
|
|
28160
|
+
if (METHOD_SET.has(entry)) known.push(entry);
|
|
28161
|
+
else unknown.push(entry);
|
|
28162
|
+
}
|
|
28163
|
+
const ordered = HTTP_METHODS.filter((method) => known.includes(method));
|
|
28164
|
+
const changes = unknown.length > 0 ? [`Dropped unrecognized request methods: ${unknown.join(", ")}.`] : [];
|
|
28165
|
+
return { ok: true, values: ordered, changes };
|
|
28166
|
+
}
|
|
28167
|
+
function intersect(current, next) {
|
|
28168
|
+
if (current === void 0) return [...next];
|
|
28169
|
+
const allowed = new Set(next);
|
|
28170
|
+
return current.filter((entry) => allowed.has(entry));
|
|
28171
|
+
}
|
|
28172
|
+
var BASE_CHANGE = "Replace was imported as a redirect that substitutes the first literal occurrence.";
|
|
28173
|
+
function mapReplace(source, from, to) {
|
|
28174
|
+
if (from.length === 0) {
|
|
28175
|
+
return { ok: false, reason: "The replace text is empty." };
|
|
28176
|
+
}
|
|
28177
|
+
if (isRequestlyRegexLiteral(from)) {
|
|
28178
|
+
return {
|
|
28179
|
+
ok: false,
|
|
28180
|
+
reason: "A regular-expression replace cannot be represented as one redirect destination."
|
|
28181
|
+
};
|
|
28182
|
+
}
|
|
28183
|
+
if (to.includes("\\")) {
|
|
28184
|
+
return {
|
|
28185
|
+
ok: false,
|
|
28186
|
+
reason: "The replacement contains a backslash, which Rogatio would read as a capture reference."
|
|
28187
|
+
};
|
|
28188
|
+
}
|
|
28189
|
+
const key = ownString3(source, "key");
|
|
28190
|
+
const operator = ownString3(source, "operator");
|
|
28191
|
+
const value = ownString3(source, "value");
|
|
28192
|
+
if (key === void 0 || operator === void 0 || value === void 0) {
|
|
28193
|
+
return { ok: false, reason: "The source condition is missing." };
|
|
28194
|
+
}
|
|
28195
|
+
if (operator === "Matches") {
|
|
28196
|
+
return {
|
|
28197
|
+
ok: false,
|
|
28198
|
+
reason: "Replace combined with a Matches source cannot be represented as one redirect destination."
|
|
28199
|
+
};
|
|
28200
|
+
}
|
|
28201
|
+
const changes = [BASE_CHANGE];
|
|
28202
|
+
if (to.includes("$")) {
|
|
28203
|
+
changes.push(
|
|
28204
|
+
"A literal $ in the replacement was escaped so it is not read as a capture reference."
|
|
28205
|
+
);
|
|
28206
|
+
}
|
|
28207
|
+
const replacement = escapeDollars(to);
|
|
28208
|
+
if (key === "Url" && operator === "Equals") {
|
|
28209
|
+
return mapEquals(value, from, replacement, changes);
|
|
28210
|
+
}
|
|
28211
|
+
if (key === "Url" && operator === "Wildcard_Matches") {
|
|
28212
|
+
return mapWildcard(value, from, replacement, changes);
|
|
28213
|
+
}
|
|
28214
|
+
if (key === "Url" && operator === "Contains") {
|
|
28215
|
+
return mapContains(value, from, replacement, changes);
|
|
28216
|
+
}
|
|
28217
|
+
if (key === "host" && operator === "Equals") {
|
|
28218
|
+
return mapHostEquals(value, from, replacement, changes);
|
|
28219
|
+
}
|
|
28220
|
+
return {
|
|
28221
|
+
ok: false,
|
|
28222
|
+
reason: `Replace is not supported for source ${key} ${operator}.`
|
|
28223
|
+
};
|
|
28224
|
+
}
|
|
28225
|
+
function mapEquals(url, from, to, changes) {
|
|
28226
|
+
if (!isAbsoluteHttpUrl(url)) {
|
|
28227
|
+
return {
|
|
28228
|
+
ok: false,
|
|
28229
|
+
reason: "The exact URL is not an absolute http(s) URL."
|
|
28230
|
+
};
|
|
28231
|
+
}
|
|
28232
|
+
const index = url.indexOf(from);
|
|
28233
|
+
if (index < 0) {
|
|
28234
|
+
return {
|
|
28235
|
+
ok: false,
|
|
28236
|
+
reason: "The URL does not contain the text to replace."
|
|
28237
|
+
};
|
|
28238
|
+
}
|
|
28239
|
+
const destination = escapeDollars(
|
|
28240
|
+
url.slice(0, index) + unescapeDollars(to) + url.slice(index + from.length)
|
|
28241
|
+
);
|
|
28242
|
+
const pattern = `^${escapeRegex(url)}$`;
|
|
28243
|
+
const redirect = acceptRedirect(pattern, destination, "");
|
|
28244
|
+
if (redirect === null) {
|
|
28245
|
+
return {
|
|
28246
|
+
ok: false,
|
|
28247
|
+
reason: "The replacement does not produce an absolute http(s) redirect destination."
|
|
28248
|
+
};
|
|
28249
|
+
}
|
|
28250
|
+
return { ok: true, redirects: [redirect], changes };
|
|
28251
|
+
}
|
|
28252
|
+
function mapWildcard(pattern, from, to, changes) {
|
|
28253
|
+
if (!pattern.startsWith("http://") && !pattern.startsWith("https://")) {
|
|
28254
|
+
return {
|
|
28255
|
+
ok: false,
|
|
28256
|
+
reason: "A wildcard replace must start with an absolute http(s) URL."
|
|
28257
|
+
};
|
|
28258
|
+
}
|
|
28259
|
+
const pieces = pattern.split("*");
|
|
28260
|
+
if (pieces.length - 1 > LIMITS.maxCaptureGroups) {
|
|
28261
|
+
return {
|
|
28262
|
+
ok: false,
|
|
28263
|
+
reason: "The wildcard has more than 9 captures."
|
|
28264
|
+
};
|
|
28265
|
+
}
|
|
28266
|
+
const replaced = replaceFirstLiteral(pieces, from, unescapeDollars(to));
|
|
28267
|
+
if (replaced === null) {
|
|
28268
|
+
return {
|
|
28269
|
+
ok: false,
|
|
28270
|
+
reason: "The text to replace is not a fixed part of the URL."
|
|
28271
|
+
};
|
|
28272
|
+
}
|
|
28273
|
+
const source = `^${pieces.map((part) => escapeRegex(part)).join("(.*?)")}$`;
|
|
28274
|
+
const destination = templateFromPieces(replaced);
|
|
28275
|
+
const redirect = acceptRedirect(source, destination, "");
|
|
28276
|
+
if (redirect === null) {
|
|
28277
|
+
return {
|
|
28278
|
+
ok: false,
|
|
28279
|
+
reason: "The replacement does not produce an absolute http(s) redirect destination."
|
|
28280
|
+
};
|
|
28281
|
+
}
|
|
28282
|
+
return { ok: true, redirects: [redirect], changes };
|
|
28283
|
+
}
|
|
28284
|
+
function mapContains(contains, from, to, changes) {
|
|
28285
|
+
changes.push("Separate http and https rules were created.");
|
|
28286
|
+
const lookahead = contains.length > 0 && contains !== from ? `(?=.*${escapeRegex(contains)})` : "";
|
|
28287
|
+
const redirects = [];
|
|
28288
|
+
for (const scheme of ["https", "http"]) {
|
|
28289
|
+
const pattern = `^${lookahead}${scheme}://(.*?)${escapeRegex(from)}(.*)$`;
|
|
28290
|
+
const destination = `${scheme}://\\1${to}\\2`;
|
|
28291
|
+
const redirect = acceptRedirect(pattern, destination, scheme);
|
|
28292
|
+
if (redirect !== null) redirects.push(redirect);
|
|
28293
|
+
}
|
|
28294
|
+
if (redirects.length === 0) {
|
|
28295
|
+
return {
|
|
28296
|
+
ok: false,
|
|
28297
|
+
reason: "The replacement does not produce an absolute http(s) redirect destination."
|
|
28298
|
+
};
|
|
28299
|
+
}
|
|
28300
|
+
return { ok: true, redirects, changes };
|
|
28301
|
+
}
|
|
28302
|
+
function mapHostEquals(host, from, to, changes) {
|
|
28303
|
+
if (host.includes("/") || host.includes("?") || host.includes("#")) {
|
|
28304
|
+
return {
|
|
28305
|
+
ok: false,
|
|
28306
|
+
reason: "The host value is not a literal hostname or hostname:port."
|
|
28307
|
+
};
|
|
28308
|
+
}
|
|
28309
|
+
changes.push("Separate http and https rules were created.");
|
|
28310
|
+
const redirects = [];
|
|
28311
|
+
const schemes = [
|
|
28312
|
+
{ scheme: "https", port: host.includes(":") ? "" : "(?::443)?" },
|
|
28313
|
+
{ scheme: "http", port: host.includes(":") ? "" : "(?::80)?" }
|
|
28314
|
+
];
|
|
28315
|
+
for (const entry of schemes) {
|
|
28316
|
+
const prefix = `${entry.scheme}://${host}`;
|
|
28317
|
+
const index = prefix.indexOf(from);
|
|
28318
|
+
if (index < 0) continue;
|
|
28319
|
+
const nextPrefix = prefix.slice(0, index) + unescapeDollars(to) + prefix.slice(index + from.length);
|
|
28320
|
+
const pattern = `^${escapeRegex(entry.scheme)}:\\/\\/${escapeRegex(host)}${entry.port}((?:[/?#].*)?)$`;
|
|
28321
|
+
const destination = `${escapeDollars(nextPrefix)}\\1`;
|
|
28322
|
+
const redirect = acceptRedirect(pattern, destination, entry.scheme);
|
|
28323
|
+
if (redirect !== null) redirects.push(redirect);
|
|
28324
|
+
}
|
|
28325
|
+
if (redirects.length === 0) {
|
|
28326
|
+
return {
|
|
28327
|
+
ok: false,
|
|
28328
|
+
reason: "The text to replace is not a fixed part of the host."
|
|
28329
|
+
};
|
|
28330
|
+
}
|
|
28331
|
+
return { ok: true, redirects, changes };
|
|
28332
|
+
}
|
|
28333
|
+
function acceptRedirect(pattern, destination, nameSuffix) {
|
|
28334
|
+
if (!acceptRegex(pattern).ok) return null;
|
|
28335
|
+
if (destination.length === 0 || destination.length > LIMITS.maxRedirectDestinationLength) {
|
|
28336
|
+
return null;
|
|
28337
|
+
}
|
|
28338
|
+
if (validateRedirectDestination(destination, pattern).length > 0) return null;
|
|
28339
|
+
return {
|
|
28340
|
+
source: { key: "url", operator: "regex", value: pattern },
|
|
28341
|
+
destination,
|
|
28342
|
+
nameSuffix
|
|
28343
|
+
};
|
|
28344
|
+
}
|
|
28345
|
+
function replaceFirstLiteral(pieces, from, to) {
|
|
28346
|
+
const next = [...pieces];
|
|
28347
|
+
for (let index = 0; index < next.length; index += 1) {
|
|
28348
|
+
const piece = next[index] ?? "";
|
|
28349
|
+
const at = piece.indexOf(from);
|
|
28350
|
+
if (at < 0) continue;
|
|
28351
|
+
next[index] = piece.slice(0, at) + to + piece.slice(at + from.length);
|
|
28352
|
+
return next;
|
|
28353
|
+
}
|
|
28354
|
+
return null;
|
|
28355
|
+
}
|
|
28356
|
+
function templateFromPieces(pieces) {
|
|
28357
|
+
let template = "";
|
|
28358
|
+
for (let index = 0; index < pieces.length; index += 1) {
|
|
28359
|
+
template += escapeDollars(pieces[index] ?? "");
|
|
28360
|
+
if (index < pieces.length - 1) template += `\\${index + 1}`;
|
|
28361
|
+
}
|
|
28362
|
+
return template;
|
|
28363
|
+
}
|
|
28364
|
+
function isAbsoluteHttpUrl(value) {
|
|
28365
|
+
try {
|
|
28366
|
+
const url = new URL(value);
|
|
28367
|
+
return (url.protocol === "http:" || url.protocol === "https:") && url.username.length === 0 && url.password.length === 0 && url.hostname.length > 0 && !url.hostname.includes("*");
|
|
28368
|
+
} catch {
|
|
28369
|
+
return false;
|
|
28370
|
+
}
|
|
28371
|
+
}
|
|
28372
|
+
function escapeDollars(value) {
|
|
28373
|
+
return value.replaceAll("$", "$$");
|
|
28374
|
+
}
|
|
28375
|
+
function unescapeDollars(value) {
|
|
28376
|
+
return value.replaceAll("$$", "$");
|
|
28377
|
+
}
|
|
28378
|
+
var REQUEST_BODY_METHODS = ["POST", "PUT", "PATCH"];
|
|
28379
|
+
var DYNAMIC_FUNCTION = /rq_[A-Za-z0-9_]*\s*\(/;
|
|
28380
|
+
function mapRequestlyRule(rule) {
|
|
28381
|
+
const ruleType = ownString3(rule, "ruleType") ?? "";
|
|
28382
|
+
if (ruleType === "Cancel") {
|
|
28383
|
+
return { status: "skipped", reason: "Cancel rules are not supported." };
|
|
28384
|
+
}
|
|
28385
|
+
if (ruleType === "Delay") {
|
|
28386
|
+
return { status: "skipped", reason: "Delay rules are not supported." };
|
|
28387
|
+
}
|
|
28388
|
+
if (ruleType === "Script") {
|
|
28389
|
+
return { status: "skipped", reason: "Script rules are not supported." };
|
|
28390
|
+
}
|
|
28391
|
+
if (ruleType === "Redirect") return mapRedirect(rule);
|
|
28392
|
+
if (ruleType === "Replace") return mapReplaceRule(rule);
|
|
28393
|
+
if (ruleType === "QueryParam") return mapQuery(rule);
|
|
28394
|
+
if (ruleType === "Headers") return mapHeaders(rule);
|
|
28395
|
+
if (ruleType === "UserAgent") return mapUserAgent(rule);
|
|
28396
|
+
if (ruleType === "Request") return mapRequest(rule);
|
|
28397
|
+
if (ruleType === "Response") return mapResponse(rule);
|
|
28398
|
+
if (ruleType.length === 0) {
|
|
28399
|
+
return { status: "skipped", reason: "The rule type is missing." };
|
|
28400
|
+
}
|
|
28401
|
+
return {
|
|
28402
|
+
status: "skipped",
|
|
28403
|
+
reason: `Unsupported rule type "${ruleType}".`
|
|
28404
|
+
};
|
|
28405
|
+
}
|
|
28406
|
+
function mapRedirect(rule) {
|
|
28407
|
+
const pairs = readPairs(rule);
|
|
28408
|
+
if (!pairs.ok) return { status: "skipped", reason: pairs.reason };
|
|
28409
|
+
const drafts = [];
|
|
28410
|
+
const changes = [];
|
|
28411
|
+
const failures = [];
|
|
28412
|
+
if (ownBoolean(rule, "preserveCookie") === true) {
|
|
28413
|
+
changes.push("preserveCookie was not imported.");
|
|
28414
|
+
}
|
|
28415
|
+
for (const pair of pairs.pairs) {
|
|
28416
|
+
const built = buildRedirectPair(pair);
|
|
28417
|
+
if (!built.ok) {
|
|
28418
|
+
failures.push(built.reason);
|
|
28419
|
+
continue;
|
|
28420
|
+
}
|
|
28421
|
+
changes.push(...built.changes);
|
|
28422
|
+
drafts.push(...built.drafts);
|
|
28423
|
+
}
|
|
28424
|
+
if (drafts.length === 0) {
|
|
28425
|
+
return {
|
|
28426
|
+
status: "skipped",
|
|
28427
|
+
reason: failures[0] ?? "The redirect rule has no supported pairs."
|
|
28428
|
+
};
|
|
28429
|
+
}
|
|
28430
|
+
if (pairs.pairs.length > 1) {
|
|
28431
|
+
changes.unshift(
|
|
28432
|
+
"Redirect pairs were split into separate rules. Requestly applies pairs in sequence to the rewritten URL; each imported rule matches the original request."
|
|
28433
|
+
);
|
|
28434
|
+
}
|
|
28435
|
+
return { status: "mapped", drafts: tagIds(drafts), changes: dedupe(changes) };
|
|
28436
|
+
}
|
|
28437
|
+
function buildRedirectPair(pair) {
|
|
28438
|
+
const destinationType = ownString3(pair, "destinationType") ?? "url";
|
|
28439
|
+
if (destinationType === "map_local" || destinationType === "mock_or_file_picker") {
|
|
28440
|
+
return {
|
|
28441
|
+
ok: false,
|
|
28442
|
+
reason: "A local-file or mock redirect destination is not supported."
|
|
28443
|
+
};
|
|
28444
|
+
}
|
|
28445
|
+
if (destinationType !== "url") {
|
|
28446
|
+
return {
|
|
28447
|
+
ok: false,
|
|
28448
|
+
reason: `Unsupported redirect destination type "${destinationType}".`
|
|
28449
|
+
};
|
|
28450
|
+
}
|
|
28451
|
+
const destination = ownString3(pair, "destination");
|
|
28452
|
+
if (destination === void 0 || destination.length === 0) {
|
|
28453
|
+
return { ok: false, reason: "The redirect destination is missing." };
|
|
28454
|
+
}
|
|
28455
|
+
const prepared = preparePair(pair);
|
|
28456
|
+
if (!prepared.ok) return prepared;
|
|
28457
|
+
const rewritten = rewriteDestination(destination);
|
|
28458
|
+
const issues = validateRedirectDestination(
|
|
28459
|
+
rewritten.destination,
|
|
28460
|
+
prepared.source.value
|
|
28461
|
+
);
|
|
28462
|
+
if (issues.length > 0) {
|
|
28463
|
+
return {
|
|
28464
|
+
ok: false,
|
|
28465
|
+
reason: "The redirect destination is not an absolute http(s) URL. Capture groups cannot stand in for the scheme or host."
|
|
28466
|
+
};
|
|
28467
|
+
}
|
|
28468
|
+
const applied = applyMethods(prepared, {
|
|
28469
|
+
source: prepared.base.source,
|
|
28470
|
+
resourceTypes: prepared.base.resourceTypes,
|
|
28471
|
+
type: "redirect",
|
|
28472
|
+
redirect: { destination: rewritten.destination }
|
|
28473
|
+
});
|
|
28474
|
+
const changes = [
|
|
28475
|
+
...prepared.changes,
|
|
28476
|
+
...rewritten.changes,
|
|
28477
|
+
...applied.changes
|
|
28478
|
+
];
|
|
28479
|
+
noteDynamic(destination, changes);
|
|
28480
|
+
return { ok: true, changes, drafts: applied.drafts };
|
|
28481
|
+
}
|
|
28482
|
+
function mapReplaceRule(rule) {
|
|
28483
|
+
const pairs = readPairs(rule);
|
|
28484
|
+
if (!pairs.ok) return { status: "skipped", reason: pairs.reason };
|
|
28485
|
+
const drafts = [];
|
|
28486
|
+
const changes = [];
|
|
28487
|
+
const failures = [];
|
|
28488
|
+
for (const pair of pairs.pairs) {
|
|
28489
|
+
const source = pairSource(pair);
|
|
28490
|
+
if (!source.ok) {
|
|
28491
|
+
failures.push(source.reason);
|
|
28492
|
+
continue;
|
|
28493
|
+
}
|
|
28494
|
+
const from = ownString3(pair, "from");
|
|
28495
|
+
const to = ownString3(pair, "to");
|
|
28496
|
+
if (from === void 0 || to === void 0) {
|
|
28497
|
+
failures.push("The replace pair is missing from or to.");
|
|
28498
|
+
continue;
|
|
28499
|
+
}
|
|
28500
|
+
const replaced = mapReplace(source.source, from, to);
|
|
28501
|
+
if (!replaced.ok) {
|
|
28502
|
+
failures.push(replaced.reason);
|
|
28503
|
+
continue;
|
|
28504
|
+
}
|
|
28505
|
+
const prepared = preparePair(pair);
|
|
28506
|
+
if (!prepared.ok) {
|
|
28507
|
+
failures.push(prepared.reason);
|
|
28508
|
+
continue;
|
|
28509
|
+
}
|
|
28510
|
+
changes.push(...prepared.filterChanges, ...replaced.changes);
|
|
28511
|
+
for (const redirect of replaced.redirects) {
|
|
28512
|
+
const applied = applyMethods(
|
|
28513
|
+
prepared,
|
|
28514
|
+
{
|
|
28515
|
+
source: redirect.source,
|
|
28516
|
+
resourceTypes: prepared.base.resourceTypes,
|
|
28517
|
+
type: "redirect",
|
|
28518
|
+
redirect: { destination: redirect.destination }
|
|
28519
|
+
},
|
|
28520
|
+
redirect.nameSuffix
|
|
28521
|
+
);
|
|
28522
|
+
changes.push(...applied.changes);
|
|
28523
|
+
drafts.push(...applied.drafts);
|
|
28524
|
+
}
|
|
28525
|
+
}
|
|
28526
|
+
if (drafts.length === 0) {
|
|
28527
|
+
return {
|
|
28528
|
+
status: "skipped",
|
|
28529
|
+
reason: failures[0] ?? "The replace rule has no supported pairs."
|
|
28530
|
+
};
|
|
28531
|
+
}
|
|
28532
|
+
if (pairs.pairs.length > 1) {
|
|
28533
|
+
changes.unshift(
|
|
28534
|
+
"Replace pairs were split into separate rules. Requestly applies pairs in sequence to the rewritten URL."
|
|
28535
|
+
);
|
|
28536
|
+
}
|
|
28537
|
+
return { status: "mapped", drafts: tagIds(drafts), changes: dedupe(changes) };
|
|
28538
|
+
}
|
|
28539
|
+
function mapQuery(rule) {
|
|
28540
|
+
const pairs = readPairs(rule);
|
|
28541
|
+
if (!pairs.ok) return { status: "skipped", reason: pairs.reason };
|
|
28542
|
+
const drafts = [];
|
|
28543
|
+
const changes = [];
|
|
28544
|
+
const failures = [];
|
|
28545
|
+
for (const pair of pairs.pairs) {
|
|
28546
|
+
const prepared = preparePair(pair);
|
|
28547
|
+
if (!prepared.ok) {
|
|
28548
|
+
failures.push(prepared.reason);
|
|
28549
|
+
continue;
|
|
28550
|
+
}
|
|
28551
|
+
const mods = readModificationList(pair);
|
|
28552
|
+
if (!mods.ok) {
|
|
28553
|
+
failures.push(mods.reason);
|
|
28554
|
+
continue;
|
|
28555
|
+
}
|
|
28556
|
+
if (mods.mods.some((mod) => ownString3(mod, "type") === "Remove All")) {
|
|
28557
|
+
failures.push("Remove All query parameters is not supported.");
|
|
28558
|
+
continue;
|
|
28559
|
+
}
|
|
28560
|
+
const params = [];
|
|
28561
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28562
|
+
const pairFailures = [];
|
|
28563
|
+
let droppedDuplicate = false;
|
|
28564
|
+
for (const mod of mods.mods) {
|
|
28565
|
+
const type = ownString3(mod, "type");
|
|
28566
|
+
const name = ownString3(mod, "param");
|
|
28567
|
+
if (name === void 0 || name.length === 0 || name.length > LIMITS.maxQueryNameLength) {
|
|
28568
|
+
pairFailures.push("A query parameter name is missing or too long.");
|
|
28569
|
+
continue;
|
|
28570
|
+
}
|
|
28571
|
+
if (type === "Remove") {
|
|
28572
|
+
if (seen.has(name)) droppedDuplicate = true;
|
|
28573
|
+
seen.add(name);
|
|
28574
|
+
params.push({ name, operation: "remove" });
|
|
28575
|
+
continue;
|
|
28576
|
+
}
|
|
28577
|
+
if (type !== "Add") {
|
|
28578
|
+
pairFailures.push(
|
|
28579
|
+
type === void 0 ? "A query modification type is missing." : `Unsupported query modification "${type}".`
|
|
28580
|
+
);
|
|
28581
|
+
continue;
|
|
28582
|
+
}
|
|
28583
|
+
const value = ownString3(mod, "value") ?? "";
|
|
28584
|
+
if (value.length === 0 || value.length > LIMITS.maxQueryValueLength) {
|
|
28585
|
+
pairFailures.push("A query parameter value is missing or too long.");
|
|
28586
|
+
continue;
|
|
28587
|
+
}
|
|
28588
|
+
if (hasLoneSurrogate(value)) {
|
|
28589
|
+
pairFailures.push(
|
|
28590
|
+
"A query parameter value contains an invalid character."
|
|
28591
|
+
);
|
|
28592
|
+
continue;
|
|
28593
|
+
}
|
|
28594
|
+
const action = ownString3(mod, "actionWhenParamExists");
|
|
28595
|
+
if (action === "Ignore") {
|
|
28596
|
+
changes.push(
|
|
28597
|
+
"Add with Ignore was imported as set, which overwrites an existing parameter. Requestly leaves an existing parameter unchanged."
|
|
28598
|
+
);
|
|
28599
|
+
} else if (action !== "Overwrite") {
|
|
28600
|
+
changes.push(
|
|
28601
|
+
"Add was imported as set, which overwrites an existing parameter instead of appending another value."
|
|
28602
|
+
);
|
|
28603
|
+
}
|
|
28604
|
+
const escaped = escapeDollars2(value);
|
|
28605
|
+
if (escaped !== value) {
|
|
28606
|
+
changes.push(
|
|
28607
|
+
"A literal $ in a query value was escaped so it is not read as a capture reference."
|
|
28608
|
+
);
|
|
28609
|
+
}
|
|
28610
|
+
noteDynamic(value, changes);
|
|
28611
|
+
if (seen.has(name)) droppedDuplicate = true;
|
|
28612
|
+
seen.add(name);
|
|
28613
|
+
params.push({ name, operation: "set", value: escaped });
|
|
28614
|
+
}
|
|
28615
|
+
if (droppedDuplicate) {
|
|
28616
|
+
changes.push(
|
|
28617
|
+
"An earlier modification of the same query parameter was dropped."
|
|
28618
|
+
);
|
|
28619
|
+
}
|
|
28620
|
+
const deduped = lastParamWins(params);
|
|
28621
|
+
if (deduped.length === 0) continue;
|
|
28622
|
+
const limited = deduped.slice(0, LIMITS.maxQueryParamsPerRule);
|
|
28623
|
+
if (limited.length < deduped.length) {
|
|
28624
|
+
changes.push(
|
|
28625
|
+
`Query parameters after the first ${LIMITS.maxQueryParamsPerRule} were dropped.`
|
|
28626
|
+
);
|
|
28627
|
+
}
|
|
28628
|
+
if (limited.length === 0) {
|
|
28629
|
+
failures.push(
|
|
28630
|
+
...pairFailures,
|
|
28631
|
+
"The query rule has no supported modifications."
|
|
28632
|
+
);
|
|
28633
|
+
continue;
|
|
28634
|
+
}
|
|
28635
|
+
for (const failure2 of pairFailures) {
|
|
28636
|
+
changes.push(`Dropped a query modification: ${failure2}`);
|
|
28637
|
+
}
|
|
28638
|
+
const applied = applyMethods(prepared, {
|
|
28639
|
+
source: prepared.base.source,
|
|
28640
|
+
resourceTypes: prepared.base.resourceTypes,
|
|
28641
|
+
type: "query",
|
|
28642
|
+
action: { type: "query", params: limited }
|
|
28643
|
+
});
|
|
28644
|
+
changes.push(...prepared.changes, ...applied.changes);
|
|
28645
|
+
drafts.push(...applied.drafts);
|
|
28646
|
+
}
|
|
28647
|
+
if (drafts.length === 0) {
|
|
28648
|
+
return {
|
|
28649
|
+
status: "skipped",
|
|
28650
|
+
reason: failures[0] ?? "The query rule has no supported modifications."
|
|
28651
|
+
};
|
|
28652
|
+
}
|
|
28653
|
+
if (drafts.length > 1) {
|
|
28654
|
+
changes.unshift(
|
|
28655
|
+
"Query pairs were split into separate rules. Requestly applies every matching pair."
|
|
28656
|
+
);
|
|
28657
|
+
}
|
|
28658
|
+
return { status: "mapped", drafts: tagIds(drafts), changes: dedupe(changes) };
|
|
28659
|
+
}
|
|
28660
|
+
function mapHeaders(rule) {
|
|
28661
|
+
const pairs = readPairs(rule);
|
|
28662
|
+
if (!pairs.ok) return { status: "skipped", reason: pairs.reason };
|
|
28663
|
+
const drafts = [];
|
|
28664
|
+
const changes = [];
|
|
28665
|
+
const failures = [];
|
|
28666
|
+
for (const pair of pairs.pairs) {
|
|
28667
|
+
const prepared = preparePair(pair);
|
|
28668
|
+
if (!prepared.ok) {
|
|
28669
|
+
failures.push(prepared.reason);
|
|
28670
|
+
continue;
|
|
28671
|
+
}
|
|
28672
|
+
const mods = headerModifications(pair);
|
|
28673
|
+
if (mods.length === 0) {
|
|
28674
|
+
failures.push("The header rule has no supported modifications.");
|
|
28675
|
+
continue;
|
|
28676
|
+
}
|
|
28677
|
+
const pairFailures = [];
|
|
28678
|
+
let produced = 0;
|
|
28679
|
+
for (const mod of mods) {
|
|
28680
|
+
const header = ownString3(mod.record, "header");
|
|
28681
|
+
const type = ownString3(mod.record, "type");
|
|
28682
|
+
if (header === void 0 || header.length === 0 || header.length > LIMITS.maxHeaderNameLength) {
|
|
28683
|
+
pairFailures.push("A header name is missing or too long.");
|
|
28684
|
+
continue;
|
|
28685
|
+
}
|
|
28686
|
+
if (isForbiddenHeader(header, mod.direction)) {
|
|
28687
|
+
pairFailures.push(
|
|
28688
|
+
`Header "${header}" is forbidden on ${mod.direction} headers.`
|
|
28689
|
+
);
|
|
28690
|
+
continue;
|
|
28691
|
+
}
|
|
28692
|
+
const operation = headerOperation(type);
|
|
28693
|
+
if (operation === null) {
|
|
28694
|
+
pairFailures.push(
|
|
28695
|
+
type === void 0 ? "A header modification type is missing." : `Unsupported header modification "${type}".`
|
|
28696
|
+
);
|
|
28697
|
+
continue;
|
|
28698
|
+
}
|
|
28699
|
+
if (operation.change !== void 0) changes.push(operation.change);
|
|
28700
|
+
const value = ownString3(mod.record, "value") ?? "";
|
|
28701
|
+
if (operation.kind !== "remove") {
|
|
28702
|
+
if (value.length > LIMITS.maxHeaderValueLength) {
|
|
28703
|
+
pairFailures.push("A header value exceeds the length limit.");
|
|
28704
|
+
continue;
|
|
28705
|
+
}
|
|
28706
|
+
if (hasLoneSurrogate(value)) {
|
|
28707
|
+
pairFailures.push("A header value contains an invalid character.");
|
|
28708
|
+
continue;
|
|
28709
|
+
}
|
|
28710
|
+
}
|
|
28711
|
+
const escaped = escapeDollars2(value);
|
|
28712
|
+
if (operation.kind !== "remove" && escaped !== value) {
|
|
28713
|
+
changes.push(
|
|
28714
|
+
"A literal $ in a header value was escaped so it is not read as a capture reference."
|
|
28715
|
+
);
|
|
28716
|
+
}
|
|
28717
|
+
if (operation.kind !== "remove") noteDynamic(value, changes);
|
|
28718
|
+
const suffix = `${mod.direction} ${header}`;
|
|
28719
|
+
const applied = applyMethods(
|
|
28720
|
+
prepared,
|
|
28721
|
+
{
|
|
28722
|
+
source: prepared.base.source,
|
|
28723
|
+
resourceTypes: prepared.base.resourceTypes,
|
|
28724
|
+
type: "header",
|
|
28725
|
+
headerDirection: mod.direction,
|
|
28726
|
+
headerOperation: operation.kind,
|
|
28727
|
+
headerName: header,
|
|
28728
|
+
...operation.kind === "remove" ? {} : { headerValue: escaped }
|
|
28729
|
+
},
|
|
28730
|
+
suffix
|
|
28731
|
+
);
|
|
28732
|
+
changes.push(...applied.changes);
|
|
28733
|
+
drafts.push(...applied.drafts);
|
|
28734
|
+
produced += 1;
|
|
28735
|
+
}
|
|
28736
|
+
if (produced > 0) {
|
|
28737
|
+
changes.push(...prepared.changes);
|
|
28738
|
+
for (const failure2 of pairFailures) {
|
|
28739
|
+
changes.push(`Dropped a header modification: ${failure2}`);
|
|
28740
|
+
}
|
|
28741
|
+
} else {
|
|
28742
|
+
failures.push(...pairFailures);
|
|
28743
|
+
}
|
|
28744
|
+
if (produced > 1) {
|
|
28745
|
+
changes.push("Header modifications were split into one rule per header.");
|
|
28746
|
+
}
|
|
28747
|
+
}
|
|
28748
|
+
if (drafts.length === 0) {
|
|
28749
|
+
return {
|
|
28750
|
+
status: "skipped",
|
|
28751
|
+
reason: failures[0] ?? "The header rule has no supported modifications."
|
|
28752
|
+
};
|
|
28753
|
+
}
|
|
28754
|
+
const tagged = tagIds(drafts);
|
|
28755
|
+
if (tagged.length === 1 && tagged[0] !== void 0) {
|
|
28756
|
+
return {
|
|
28757
|
+
status: "mapped",
|
|
28758
|
+
drafts: [{ ...tagged[0], nameSuffix: "", keepId: true }],
|
|
28759
|
+
changes: dedupe(changes)
|
|
28760
|
+
};
|
|
28761
|
+
}
|
|
28762
|
+
return { status: "mapped", drafts: tagged, changes: dedupe(changes) };
|
|
28763
|
+
}
|
|
28764
|
+
function mapUserAgent(rule) {
|
|
28765
|
+
const pairs = readPairs(rule);
|
|
28766
|
+
if (!pairs.ok) return { status: "skipped", reason: pairs.reason };
|
|
28767
|
+
const drafts = [];
|
|
28768
|
+
const changes = [];
|
|
28769
|
+
const failures = [];
|
|
28770
|
+
for (const pair of pairs.pairs) {
|
|
28771
|
+
const prepared = preparePair(pair);
|
|
28772
|
+
if (!prepared.ok) {
|
|
28773
|
+
failures.push(prepared.reason);
|
|
28774
|
+
continue;
|
|
28775
|
+
}
|
|
28776
|
+
const userAgent = ownString3(pair, "userAgent");
|
|
28777
|
+
if (userAgent === void 0 || userAgent.length === 0) {
|
|
28778
|
+
failures.push("The user-agent string is missing.");
|
|
28779
|
+
continue;
|
|
28780
|
+
}
|
|
28781
|
+
if (userAgent.length > LIMITS.maxHeaderValueLength || hasLoneSurrogate(userAgent)) {
|
|
28782
|
+
failures.push("The user-agent string is too long or invalid.");
|
|
28783
|
+
continue;
|
|
28784
|
+
}
|
|
28785
|
+
if (isForbiddenHeader("User-Agent", "request")) {
|
|
28786
|
+
failures.push('Header "User-Agent" is forbidden on request headers.');
|
|
28787
|
+
continue;
|
|
28788
|
+
}
|
|
28789
|
+
const envType = ownString3(pair, "envType");
|
|
28790
|
+
if (envType === "browser" || envType === "device") {
|
|
28791
|
+
changes.push(
|
|
28792
|
+
"The browser or device preset was imported as the stored user-agent string."
|
|
28793
|
+
);
|
|
28794
|
+
}
|
|
28795
|
+
const escaped = escapeDollars2(userAgent);
|
|
28796
|
+
if (escaped !== userAgent) {
|
|
28797
|
+
changes.push(
|
|
28798
|
+
"A literal $ in the user-agent was escaped so it is not read as a capture reference."
|
|
28799
|
+
);
|
|
28800
|
+
}
|
|
28801
|
+
noteDynamic(userAgent, changes);
|
|
28802
|
+
const applied = applyMethods(prepared, {
|
|
28803
|
+
source: prepared.base.source,
|
|
28804
|
+
resourceTypes: prepared.base.resourceTypes,
|
|
28805
|
+
type: "header",
|
|
28806
|
+
headerDirection: "request",
|
|
28807
|
+
headerOperation: "set",
|
|
28808
|
+
headerName: "User-Agent",
|
|
28809
|
+
headerValue: escaped
|
|
28810
|
+
});
|
|
28811
|
+
changes.push(...prepared.changes, ...applied.changes);
|
|
28812
|
+
drafts.push(...applied.drafts);
|
|
28813
|
+
}
|
|
28814
|
+
if (drafts.length === 0) {
|
|
28815
|
+
return {
|
|
28816
|
+
status: "skipped",
|
|
28817
|
+
reason: failures[0] ?? "The user-agent rule has no supported pairs."
|
|
28818
|
+
};
|
|
28819
|
+
}
|
|
28820
|
+
if (drafts.length > 1) {
|
|
28821
|
+
changes.unshift("User-agent pairs were split into separate rules.");
|
|
28822
|
+
}
|
|
28823
|
+
return { status: "mapped", drafts: tagIds(drafts), changes: dedupe(changes) };
|
|
28824
|
+
}
|
|
28825
|
+
function mapRequest(rule) {
|
|
28826
|
+
return mapBody(rule, "request");
|
|
28827
|
+
}
|
|
28828
|
+
function mapResponse(rule) {
|
|
28829
|
+
return mapBody(rule, "response");
|
|
28830
|
+
}
|
|
28831
|
+
function mapBody(rule, kind) {
|
|
28832
|
+
const pairs = readPairs(rule);
|
|
28833
|
+
if (!pairs.ok) return { status: "skipped", reason: pairs.reason };
|
|
28834
|
+
const drafts = [];
|
|
28835
|
+
const changes = [];
|
|
28836
|
+
const failures = [];
|
|
28837
|
+
for (const pair of pairs.pairs) {
|
|
28838
|
+
const prepared = preparePair(pair);
|
|
28839
|
+
if (!prepared.ok) {
|
|
28840
|
+
failures.push(prepared.reason);
|
|
28841
|
+
continue;
|
|
28842
|
+
}
|
|
28843
|
+
const bodyRecord = pair[kind];
|
|
28844
|
+
if (!isRecord3(bodyRecord)) {
|
|
28845
|
+
failures.push(`The ${kind} body is missing.`);
|
|
28846
|
+
continue;
|
|
28847
|
+
}
|
|
28848
|
+
const type = ownString3(bodyRecord, "type");
|
|
28849
|
+
if (type === "code") {
|
|
28850
|
+
failures.push(`JavaScript ${kind} functions are not supported.`);
|
|
28851
|
+
continue;
|
|
28852
|
+
}
|
|
28853
|
+
if (type === "local_file") {
|
|
28854
|
+
failures.push("A local-file response is not supported.");
|
|
28855
|
+
continue;
|
|
28856
|
+
}
|
|
28857
|
+
if (type !== "static") {
|
|
28858
|
+
failures.push(
|
|
28859
|
+
type === void 0 ? `The ${kind} body type is missing.` : `Unsupported ${kind} body type "${type}".`
|
|
28860
|
+
);
|
|
28861
|
+
continue;
|
|
28862
|
+
}
|
|
28863
|
+
const value = ownString3(bodyRecord, "value") ?? "";
|
|
28864
|
+
const limit = kind === "request" ? LIMITS.maxRequestBodyBytes : LIMITS.maxResponseBodyBytes;
|
|
28865
|
+
if (value.length === 0 || value.length > limit || hasLoneSurrogate(value)) {
|
|
28866
|
+
failures.push(`The ${kind} body is empty, too large, or invalid.`);
|
|
28867
|
+
continue;
|
|
28868
|
+
}
|
|
28869
|
+
const escaped = escapeDollars2(value);
|
|
28870
|
+
if (escaped !== value) {
|
|
28871
|
+
changes.push(
|
|
28872
|
+
"A literal $ in the body was escaped so it is not read as a capture reference."
|
|
28873
|
+
);
|
|
28874
|
+
}
|
|
28875
|
+
noteDynamic(value, changes);
|
|
28876
|
+
if (kind === "response") {
|
|
28877
|
+
const status = ownString3(bodyRecord, "statusCode");
|
|
28878
|
+
const statusNote = status !== void 0 && status.length > 0 ? ` Status ${status} was not imported.` : "";
|
|
28879
|
+
changes.push(
|
|
28880
|
+
`Requestly static responses replace the response and may skip the upstream request.${statusNote} Rogatio fetches the upstream response and replaces its body, keeping the upstream status and headers.`
|
|
28881
|
+
);
|
|
28882
|
+
const applied = applyMethods(prepared, {
|
|
28883
|
+
source: prepared.base.source,
|
|
28884
|
+
resourceTypes: prepared.base.resourceTypes,
|
|
28885
|
+
type: "response-body",
|
|
28886
|
+
responseBody: { mode: "replace", body: escaped }
|
|
28887
|
+
});
|
|
28888
|
+
changes.push(...prepared.changes, ...applied.changes);
|
|
28889
|
+
drafts.push(...applied.drafts);
|
|
28890
|
+
continue;
|
|
28891
|
+
}
|
|
28892
|
+
const narrowed = narrowRequestBody(
|
|
28893
|
+
prepared.filters,
|
|
28894
|
+
prepared.base.resourceTypes
|
|
28895
|
+
);
|
|
28896
|
+
if (!narrowed.ok) {
|
|
28897
|
+
failures.push(narrowed.reason);
|
|
28898
|
+
continue;
|
|
28899
|
+
}
|
|
28900
|
+
changes.push(...prepared.changes, ...narrowed.changes);
|
|
28901
|
+
for (const method of narrowed.methods) {
|
|
28902
|
+
drafts.push(
|
|
28903
|
+
draftOf(
|
|
28904
|
+
{
|
|
28905
|
+
source: prepared.base.source,
|
|
28906
|
+
resourceTypes: ["xmlhttprequest"],
|
|
28907
|
+
method,
|
|
28908
|
+
type: "request-body",
|
|
28909
|
+
requestBody: { mode: "replace", body: escaped }
|
|
28910
|
+
},
|
|
28911
|
+
narrowed.methods.length > 1 ? method : ""
|
|
28912
|
+
)
|
|
28913
|
+
);
|
|
28914
|
+
}
|
|
28915
|
+
}
|
|
28916
|
+
if (drafts.length === 0) {
|
|
28917
|
+
return {
|
|
28918
|
+
status: "skipped",
|
|
28919
|
+
reason: failures[0] ?? `The ${kind} rule has no supported pairs.`
|
|
28920
|
+
};
|
|
28921
|
+
}
|
|
28922
|
+
if (kind === "response" && drafts.length > 1) {
|
|
28923
|
+
changes.unshift(
|
|
28924
|
+
"Response pairs were split into separate rules. Requestly runs the first pair only."
|
|
28925
|
+
);
|
|
28926
|
+
}
|
|
28927
|
+
return { status: "mapped", drafts: tagIds(drafts), changes: dedupe(changes) };
|
|
28928
|
+
}
|
|
28929
|
+
function preparePair(pair) {
|
|
28930
|
+
const sourceRecord = pairSource(pair);
|
|
28931
|
+
if (!sourceRecord.ok) return sourceRecord;
|
|
28932
|
+
const converted = convertSource(sourceRecord.source);
|
|
28933
|
+
if (!converted.ok) return converted;
|
|
28934
|
+
const filters = readFilters(sourceRecord.source);
|
|
28935
|
+
if (!filters.ok) return filters;
|
|
28936
|
+
const method = filters.filters.methods !== "all" && filters.filters.methods.length === 1 ? filters.filters.methods[0] : void 0;
|
|
28937
|
+
const sourceChanges = converted.converted.changes;
|
|
28938
|
+
const filterChanges = filters.filters.changes;
|
|
28939
|
+
return {
|
|
28940
|
+
ok: true,
|
|
28941
|
+
source: converted.converted.source,
|
|
28942
|
+
filters: filters.filters,
|
|
28943
|
+
sourceChanges,
|
|
28944
|
+
filterChanges,
|
|
28945
|
+
changes: [...sourceChanges, ...filterChanges],
|
|
28946
|
+
base: {
|
|
28947
|
+
source: converted.converted.source,
|
|
28948
|
+
resourceTypes: [...filters.filters.resourceTypes],
|
|
28949
|
+
...method === void 0 ? {} : { method }
|
|
28950
|
+
}
|
|
28951
|
+
};
|
|
28952
|
+
}
|
|
28953
|
+
function narrowRequestBody(filters, resourceTypes) {
|
|
28954
|
+
const changes = [];
|
|
28955
|
+
if (resourceTypes.length !== 1 || resourceTypes[0] !== "xmlhttprequest") {
|
|
28956
|
+
if (!resourceTypes.includes("xmlhttprequest")) {
|
|
28957
|
+
return {
|
|
28958
|
+
ok: false,
|
|
28959
|
+
reason: "Request-body rules only match xmlhttprequest, which this rule does not include."
|
|
28960
|
+
};
|
|
28961
|
+
}
|
|
28962
|
+
changes.push(
|
|
28963
|
+
"Request-body rules only match xmlhttprequest. Other resource types were dropped."
|
|
28964
|
+
);
|
|
28965
|
+
}
|
|
28966
|
+
const allowed = REQUEST_BODY_METHODS;
|
|
28967
|
+
if (filters.methods === "all") {
|
|
28968
|
+
changes.push(
|
|
28969
|
+
"The request had no method filter. It was split into POST, PUT, and PATCH, the methods Rogatio request-body rules allow."
|
|
28970
|
+
);
|
|
28971
|
+
return { ok: true, methods: [...allowed], changes };
|
|
28972
|
+
}
|
|
28973
|
+
const methods = allowed.filter((method) => filters.methods.includes(method));
|
|
28974
|
+
const dropped = filters.methods.filter(
|
|
28975
|
+
(method) => !allowed.includes(method)
|
|
28976
|
+
);
|
|
28977
|
+
if (methods.length === 0) {
|
|
28978
|
+
return {
|
|
28979
|
+
ok: false,
|
|
28980
|
+
reason: "Request-body rules only support POST, PUT, and PATCH."
|
|
28981
|
+
};
|
|
28982
|
+
}
|
|
28983
|
+
if (dropped.length > 0) {
|
|
28984
|
+
changes.push(
|
|
28985
|
+
`Dropped request methods other than POST, PUT, and PATCH: ${dropped.join(", ")}.`
|
|
28986
|
+
);
|
|
28987
|
+
}
|
|
28988
|
+
if (methods.length > 1) {
|
|
28989
|
+
changes.push(
|
|
28990
|
+
"The request was split into one rule per method because a request-body rule stores one method."
|
|
28991
|
+
);
|
|
28992
|
+
}
|
|
28993
|
+
return { ok: true, methods, changes };
|
|
28994
|
+
}
|
|
28995
|
+
function pairSource(pair) {
|
|
28996
|
+
const source = pair.source;
|
|
28997
|
+
if (!isRecord3(source)) {
|
|
28998
|
+
return { ok: false, reason: "The source condition is missing." };
|
|
28999
|
+
}
|
|
29000
|
+
return { ok: true, source };
|
|
29001
|
+
}
|
|
29002
|
+
function readPairs(rule) {
|
|
29003
|
+
const pairs = rule.pairs;
|
|
29004
|
+
if (!Array.isArray(pairs) || pairs.length === 0) {
|
|
29005
|
+
return { ok: false, reason: "The rule has no pairs." };
|
|
29006
|
+
}
|
|
29007
|
+
const records = [];
|
|
29008
|
+
for (const pair of pairs) {
|
|
29009
|
+
if (!isRecord3(pair)) {
|
|
29010
|
+
return { ok: false, reason: "A rule pair is not an object." };
|
|
29011
|
+
}
|
|
29012
|
+
records.push(pair);
|
|
29013
|
+
}
|
|
29014
|
+
return { ok: true, pairs: records };
|
|
29015
|
+
}
|
|
29016
|
+
function readModificationList(pair) {
|
|
29017
|
+
const modifications = pair.modifications;
|
|
29018
|
+
if (!Array.isArray(modifications) || modifications.length === 0) {
|
|
29019
|
+
return { ok: false, reason: "The query rule has no modifications." };
|
|
29020
|
+
}
|
|
29021
|
+
const mods = [];
|
|
29022
|
+
for (const mod of modifications) {
|
|
29023
|
+
if (!isRecord3(mod)) {
|
|
29024
|
+
return { ok: false, reason: "A query modification is not an object." };
|
|
29025
|
+
}
|
|
29026
|
+
mods.push(mod);
|
|
29027
|
+
}
|
|
29028
|
+
return { ok: true, mods };
|
|
29029
|
+
}
|
|
29030
|
+
function headerModifications(pair) {
|
|
29031
|
+
const modifications = pair.modifications;
|
|
29032
|
+
if (isRecord3(modifications)) {
|
|
29033
|
+
const found = [];
|
|
29034
|
+
for (const direction of ["Request", "Response"]) {
|
|
29035
|
+
const list = modifications[direction];
|
|
29036
|
+
if (!Array.isArray(list)) continue;
|
|
29037
|
+
for (const entry of list) {
|
|
29038
|
+
if (!isRecord3(entry)) continue;
|
|
29039
|
+
found.push({
|
|
29040
|
+
record: entry,
|
|
29041
|
+
direction: direction === "Request" ? "request" : "response"
|
|
29042
|
+
});
|
|
29043
|
+
}
|
|
29044
|
+
}
|
|
29045
|
+
if (found.length > 0) return found;
|
|
29046
|
+
}
|
|
29047
|
+
if (ownString3(pair, "header") !== void 0) {
|
|
29048
|
+
const target = ownString3(pair, "target");
|
|
29049
|
+
return [
|
|
29050
|
+
{
|
|
29051
|
+
record: pair,
|
|
29052
|
+
direction: target === "Response" ? "response" : "request"
|
|
29053
|
+
}
|
|
29054
|
+
];
|
|
29055
|
+
}
|
|
29056
|
+
return [];
|
|
29057
|
+
}
|
|
29058
|
+
function headerOperation(type) {
|
|
29059
|
+
if (type === "Add") return { kind: "append" };
|
|
29060
|
+
if (type === "Remove") return { kind: "remove" };
|
|
29061
|
+
if (type === "Modify") {
|
|
29062
|
+
return {
|
|
29063
|
+
kind: "set",
|
|
29064
|
+
change: "Modify updates an existing header only. It was imported as set, which also adds the header when it is absent."
|
|
29065
|
+
};
|
|
29066
|
+
}
|
|
29067
|
+
if (type === "Replace") {
|
|
29068
|
+
return {
|
|
29069
|
+
kind: "set",
|
|
29070
|
+
change: "Header Replace was imported as set."
|
|
29071
|
+
};
|
|
29072
|
+
}
|
|
29073
|
+
return null;
|
|
29074
|
+
}
|
|
29075
|
+
function rewriteDestination(destination) {
|
|
29076
|
+
let output = "";
|
|
29077
|
+
let captures = false;
|
|
29078
|
+
let dollars = false;
|
|
29079
|
+
for (let index = 0; index < destination.length; index += 1) {
|
|
29080
|
+
const char = destination[index];
|
|
29081
|
+
if (char !== "$") {
|
|
29082
|
+
output += char ?? "";
|
|
29083
|
+
continue;
|
|
29084
|
+
}
|
|
29085
|
+
const next = destination[index + 1];
|
|
29086
|
+
if (next !== void 0 && next >= "1" && next <= "9") {
|
|
29087
|
+
output += `\\${next}`;
|
|
29088
|
+
captures = true;
|
|
29089
|
+
index += 1;
|
|
29090
|
+
continue;
|
|
29091
|
+
}
|
|
29092
|
+
output += "$$";
|
|
29093
|
+
dollars = true;
|
|
29094
|
+
}
|
|
29095
|
+
const changes = [];
|
|
29096
|
+
if (captures) {
|
|
29097
|
+
changes.push(
|
|
29098
|
+
"Redirect capture references were rewritten from $1\u2013$9 to \\1\u2013\\9."
|
|
29099
|
+
);
|
|
29100
|
+
}
|
|
29101
|
+
if (dollars) {
|
|
29102
|
+
changes.push(
|
|
29103
|
+
"A literal $ in the redirect destination was escaped so it is not read as a capture reference."
|
|
29104
|
+
);
|
|
29105
|
+
}
|
|
29106
|
+
return { destination: output, changes };
|
|
29107
|
+
}
|
|
29108
|
+
function lastParamWins(params) {
|
|
29109
|
+
const order = [];
|
|
29110
|
+
const byName = /* @__PURE__ */ new Map();
|
|
29111
|
+
for (const param of params) {
|
|
29112
|
+
if (!byName.has(param.name)) order.push(param.name);
|
|
29113
|
+
byName.set(param.name, param);
|
|
29114
|
+
}
|
|
29115
|
+
const result = [];
|
|
29116
|
+
for (const name of order) {
|
|
29117
|
+
const param = byName.get(name);
|
|
29118
|
+
if (param !== void 0) result.push(param);
|
|
29119
|
+
}
|
|
29120
|
+
return result;
|
|
29121
|
+
}
|
|
29122
|
+
function noteDynamic(value, changes) {
|
|
29123
|
+
if (!DYNAMIC_FUNCTION.test(value)) return;
|
|
29124
|
+
const message = "A Requestly dynamic function was kept as literal text and is not evaluated.";
|
|
29125
|
+
if (!changes.includes(message)) changes.push(message);
|
|
29126
|
+
}
|
|
29127
|
+
function escapeDollars2(value) {
|
|
29128
|
+
return value.replaceAll("$", "$$");
|
|
29129
|
+
}
|
|
29130
|
+
function draftOf(rule, nameSuffix = "") {
|
|
29131
|
+
return { nameSuffix, keepId: false, rule };
|
|
29132
|
+
}
|
|
29133
|
+
function tagIds(drafts) {
|
|
29134
|
+
return drafts.map((draft, index) => ({
|
|
29135
|
+
...draft,
|
|
29136
|
+
keepId: index === 0
|
|
29137
|
+
}));
|
|
29138
|
+
}
|
|
29139
|
+
function applyMethods(prepared, rule, nameSuffix = "") {
|
|
29140
|
+
const methodFilter = prepared.filters.methods;
|
|
29141
|
+
if (methodFilter === "all" || methodFilter.length <= 1) {
|
|
29142
|
+
const method = prepared.base.method;
|
|
29143
|
+
return {
|
|
29144
|
+
changes: [],
|
|
29145
|
+
drafts: [
|
|
29146
|
+
draftOf(
|
|
29147
|
+
{
|
|
29148
|
+
...rule,
|
|
29149
|
+
...method === void 0 ? {} : { method }
|
|
29150
|
+
},
|
|
29151
|
+
nameSuffix
|
|
29152
|
+
)
|
|
29153
|
+
]
|
|
29154
|
+
};
|
|
29155
|
+
}
|
|
29156
|
+
return {
|
|
29157
|
+
changes: [
|
|
29158
|
+
`Request methods were split into separate rules (${methodFilter.join(", ")}). Rogatio stores one method on a rule.`
|
|
29159
|
+
],
|
|
29160
|
+
drafts: methodFilter.map(
|
|
29161
|
+
(method) => draftOf(
|
|
29162
|
+
{ ...rule, method },
|
|
29163
|
+
nameSuffix.length > 0 ? `${nameSuffix} ${method}` : method
|
|
29164
|
+
)
|
|
29165
|
+
)
|
|
29166
|
+
};
|
|
29167
|
+
}
|
|
29168
|
+
function dedupe(changes) {
|
|
29169
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29170
|
+
const result = [];
|
|
29171
|
+
for (const change of changes) {
|
|
29172
|
+
if (seen.has(change)) continue;
|
|
29173
|
+
seen.add(change);
|
|
29174
|
+
result.push(change);
|
|
29175
|
+
}
|
|
29176
|
+
return result;
|
|
29177
|
+
}
|
|
29178
|
+
var MAX_DEPTH = 40;
|
|
29179
|
+
var MAX_STRING = 5e6;
|
|
29180
|
+
var MAX_ARRAY = 1e5;
|
|
29181
|
+
var BANNED_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
29182
|
+
function snapshotJson(value) {
|
|
29183
|
+
return snapshot(value, /* @__PURE__ */ new WeakSet(), 0);
|
|
29184
|
+
}
|
|
29185
|
+
function snapshot(value, ancestors, depth) {
|
|
29186
|
+
if (typeof value === "string") {
|
|
29187
|
+
if (value.length > MAX_STRING) return tooLarge();
|
|
29188
|
+
return { ok: true, value };
|
|
29189
|
+
}
|
|
29190
|
+
if (typeof value === "number") {
|
|
29191
|
+
if (!Number.isFinite(value)) {
|
|
29192
|
+
return {
|
|
29193
|
+
ok: false,
|
|
29194
|
+
error: "Requestly export contains a non-finite number."
|
|
29195
|
+
};
|
|
29196
|
+
}
|
|
29197
|
+
return { ok: true, value };
|
|
29198
|
+
}
|
|
29199
|
+
if (typeof value === "boolean" || value === null) {
|
|
29200
|
+
return { ok: true, value };
|
|
29201
|
+
}
|
|
29202
|
+
if (value === void 0) return { ok: true, value: void 0 };
|
|
29203
|
+
if (typeof value !== "object") {
|
|
29204
|
+
return {
|
|
29205
|
+
ok: false,
|
|
29206
|
+
error: "Requestly export contains a value that is not JSON data."
|
|
29207
|
+
};
|
|
29208
|
+
}
|
|
29209
|
+
if (depth > MAX_DEPTH) {
|
|
29210
|
+
return { ok: false, error: "Requestly export is nested too deeply." };
|
|
29211
|
+
}
|
|
29212
|
+
if (ancestors.has(value)) {
|
|
29213
|
+
return { ok: false, error: "Requestly export contains a cycle." };
|
|
29214
|
+
}
|
|
29215
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
29216
|
+
return {
|
|
29217
|
+
ok: false,
|
|
29218
|
+
error: "Requestly export contains a value that is not JSON data."
|
|
29219
|
+
};
|
|
29220
|
+
}
|
|
29221
|
+
ancestors.add(value);
|
|
29222
|
+
try {
|
|
29223
|
+
if (Array.isArray(value)) return snapshotArray(value, ancestors, depth);
|
|
29224
|
+
return snapshotObject(value, ancestors, depth);
|
|
29225
|
+
} finally {
|
|
29226
|
+
ancestors.delete(value);
|
|
29227
|
+
}
|
|
29228
|
+
}
|
|
29229
|
+
function snapshotArray(value, ancestors, depth) {
|
|
29230
|
+
if (Object.getPrototypeOf(value) !== Array.prototype) return notJson();
|
|
29231
|
+
const length = Object.getOwnPropertyDescriptor(value, "length");
|
|
29232
|
+
if (length === void 0 || !("value" in length) || !Number.isSafeInteger(length.value) || length.value < 0 || length.value > MAX_ARRAY) {
|
|
29233
|
+
return tooLarge();
|
|
29234
|
+
}
|
|
29235
|
+
const result = [];
|
|
29236
|
+
for (let index = 0; index < length.value; index += 1) {
|
|
29237
|
+
const entry = Object.getOwnPropertyDescriptor(value, String(index));
|
|
29238
|
+
if (entry === void 0 || !("value" in entry) || !entry.enumerable) {
|
|
29239
|
+
return {
|
|
29240
|
+
ok: false,
|
|
29241
|
+
error: "Requestly export contains a sparse array."
|
|
29242
|
+
};
|
|
29243
|
+
}
|
|
29244
|
+
const child = snapshot(entry.value, ancestors, depth + 1);
|
|
29245
|
+
if (!child.ok) return child;
|
|
29246
|
+
result.push(child.value);
|
|
29247
|
+
}
|
|
29248
|
+
return { ok: true, value: result };
|
|
29249
|
+
}
|
|
29250
|
+
function snapshotObject(value, ancestors, depth) {
|
|
29251
|
+
const proto = Object.getPrototypeOf(value);
|
|
29252
|
+
if (proto !== Object.prototype && proto !== null) return notJson();
|
|
29253
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
29254
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
29255
|
+
if (BANNED_KEYS.has(key)) continue;
|
|
29256
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
29257
|
+
if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) {
|
|
29258
|
+
return {
|
|
29259
|
+
ok: false,
|
|
29260
|
+
error: "Requestly export contains a value that is not JSON data."
|
|
29261
|
+
};
|
|
29262
|
+
}
|
|
29263
|
+
if (descriptor.value === void 0) continue;
|
|
29264
|
+
const child = snapshot(descriptor.value, ancestors, depth + 1);
|
|
29265
|
+
if (!child.ok) return child;
|
|
29266
|
+
Object.defineProperty(result, key, {
|
|
29267
|
+
configurable: true,
|
|
29268
|
+
enumerable: true,
|
|
29269
|
+
writable: true,
|
|
29270
|
+
value: child.value
|
|
29271
|
+
});
|
|
29272
|
+
}
|
|
29273
|
+
return { ok: true, value: result };
|
|
29274
|
+
}
|
|
29275
|
+
function tooLarge() {
|
|
29276
|
+
return {
|
|
29277
|
+
ok: false,
|
|
29278
|
+
error: "Requestly export contains a value that is too large."
|
|
29279
|
+
};
|
|
29280
|
+
}
|
|
29281
|
+
function notJson() {
|
|
29282
|
+
return {
|
|
29283
|
+
ok: false,
|
|
29284
|
+
error: "Requestly export contains a value that is not JSON data."
|
|
29285
|
+
};
|
|
29286
|
+
}
|
|
29287
|
+
function asRecords(value) {
|
|
29288
|
+
if (!Array.isArray(value)) return null;
|
|
29289
|
+
const records = [];
|
|
29290
|
+
for (const entry of value) {
|
|
29291
|
+
if (!isRecord3(entry)) return null;
|
|
29292
|
+
records.push(entry);
|
|
29293
|
+
}
|
|
29294
|
+
return records;
|
|
29295
|
+
}
|
|
29296
|
+
function parseRequestlyExport(value) {
|
|
29297
|
+
const snapshot2 = snapshotJson(value);
|
|
29298
|
+
if (!snapshot2.ok) return snapshot2;
|
|
29299
|
+
const records = rootRecords(snapshot2.value);
|
|
29300
|
+
if (records === null) {
|
|
29301
|
+
return {
|
|
29302
|
+
ok: false,
|
|
29303
|
+
error: "Requestly export must be a JSON array of rules and groups, a single rule, or an object with a rules array."
|
|
29304
|
+
};
|
|
29305
|
+
}
|
|
29306
|
+
const groups = [];
|
|
29307
|
+
const rules = [];
|
|
29308
|
+
const unknown = [];
|
|
29309
|
+
const seenRuleIds = /* @__PURE__ */ new Set();
|
|
29310
|
+
const addRule = (record, groupId) => {
|
|
29311
|
+
const id2 = ownString3(record, "id");
|
|
29312
|
+
if (id2 !== void 0 && id2.length > 0) {
|
|
29313
|
+
if (seenRuleIds.has(id2)) return;
|
|
29314
|
+
seenRuleIds.add(id2);
|
|
29315
|
+
}
|
|
29316
|
+
const ruleType = ownString3(record, "ruleType") ?? "";
|
|
29317
|
+
rules.push({
|
|
29318
|
+
raw: record,
|
|
29319
|
+
id: id2 !== void 0 && id2.length > 0 ? id2 : void 0,
|
|
29320
|
+
name: ownString3(record, "name") ?? "",
|
|
29321
|
+
ruleType,
|
|
29322
|
+
groupId
|
|
29323
|
+
});
|
|
29324
|
+
};
|
|
29325
|
+
for (const record of records) {
|
|
29326
|
+
const objectType = ownString3(record, "objectType");
|
|
29327
|
+
if (objectType === "group") {
|
|
29328
|
+
const id2 = ownString3(record, "id");
|
|
29329
|
+
if (id2 === void 0 || id2.length === 0) {
|
|
29330
|
+
unknown.push({
|
|
29331
|
+
name: displayName(ownString3(record, "name") ?? ""),
|
|
29332
|
+
type: "group"
|
|
29333
|
+
});
|
|
29334
|
+
continue;
|
|
29335
|
+
}
|
|
29336
|
+
groups.push({
|
|
29337
|
+
id: id2,
|
|
29338
|
+
name: cleanLabel(ownString3(record, "name") ?? "", "Imported group"),
|
|
29339
|
+
status: ownString3(record, "status")
|
|
29340
|
+
});
|
|
29341
|
+
const children = record.children;
|
|
29342
|
+
if (Array.isArray(children)) {
|
|
29343
|
+
for (const child of children) {
|
|
29344
|
+
if (!isRecord3(child)) continue;
|
|
29345
|
+
const childType = ownString3(child, "objectType");
|
|
29346
|
+
const childRuleType = ownString3(child, "ruleType");
|
|
29347
|
+
if (childType === "group" || childRuleType === void 0) continue;
|
|
29348
|
+
const childGroup = ownString3(child, "groupId");
|
|
29349
|
+
addRule(
|
|
29350
|
+
child,
|
|
29351
|
+
childGroup !== void 0 && childGroup.length > 0 ? childGroup : id2
|
|
29352
|
+
);
|
|
29353
|
+
}
|
|
29354
|
+
}
|
|
29355
|
+
continue;
|
|
29356
|
+
}
|
|
29357
|
+
if (objectType === "rule" || ownString3(record, "ruleType") !== void 0) {
|
|
29358
|
+
addRule(record, ownString3(record, "groupId") ?? "");
|
|
29359
|
+
continue;
|
|
29360
|
+
}
|
|
29361
|
+
unknown.push({
|
|
29362
|
+
name: displayName(ownString3(record, "name") ?? ""),
|
|
29363
|
+
type: objectType ?? "unknown"
|
|
29364
|
+
});
|
|
29365
|
+
}
|
|
29366
|
+
return { ok: true, export: { groups, rules, unknown } };
|
|
29367
|
+
}
|
|
29368
|
+
function rootRecords(value) {
|
|
29369
|
+
if (Array.isArray(value)) return asRecords(value);
|
|
29370
|
+
if (!isRecord3(value)) return null;
|
|
29371
|
+
if (ownString3(value, "objectType") === "rule" || ownString3(value, "ruleType") !== void 0) {
|
|
29372
|
+
return [value];
|
|
29373
|
+
}
|
|
29374
|
+
if (!Object.hasOwn(value, "rules")) return null;
|
|
29375
|
+
const rules = asRecords(value.rules);
|
|
29376
|
+
if (rules === null) return null;
|
|
29377
|
+
const groupsValue = Object.hasOwn(value, "groups") ? value.groups : Object.hasOwn(value, "updatedGroups") ? value.updatedGroups : [];
|
|
29378
|
+
if (groupsValue === void 0) return rules;
|
|
29379
|
+
if (!Array.isArray(groupsValue)) return null;
|
|
29380
|
+
const groups = asRecords(groupsValue);
|
|
29381
|
+
if (groups === null) return null;
|
|
29382
|
+
return [...rules, ...groups];
|
|
29383
|
+
}
|
|
29384
|
+
var ID_PATTERN3 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
29385
|
+
var UNGROUPED_NAME = "Ungrouped";
|
|
29386
|
+
var INACTIVE_CHANGE = "The rule was inactive in Requestly. Rogatio project files have no disabled flag, so the rule is active.";
|
|
29387
|
+
var DESCRIPTION_CHANGE = "The rule description was not imported.";
|
|
29388
|
+
var MISSING_GROUP_CHANGE = "The rule's group was not in the export. It was placed in Ungrouped.";
|
|
29389
|
+
var PRIORITY_NOTE = "Priorities were assigned from 1000 downward in export order so an earlier rule wins.";
|
|
29390
|
+
var PRIORITY_FLOOR_NOTE = "More than 1000 rules were imported, so the remaining rules share priority 1.";
|
|
29391
|
+
function emptyBucket() {
|
|
29392
|
+
return {
|
|
29393
|
+
requestlyId: void 0,
|
|
29394
|
+
id: "",
|
|
29395
|
+
name: "",
|
|
29396
|
+
inactive: false,
|
|
29397
|
+
rules: []
|
|
29398
|
+
};
|
|
29399
|
+
}
|
|
29400
|
+
function importRequestlyExport(value) {
|
|
29401
|
+
const parsed = parseRequestlyExport(value);
|
|
29402
|
+
if (!parsed.ok) return parsed;
|
|
29403
|
+
const ids = /* @__PURE__ */ new Set();
|
|
29404
|
+
const names = /* @__PURE__ */ new Set();
|
|
29405
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
29406
|
+
const ordered = [];
|
|
29407
|
+
for (const group of parsed.export.groups) {
|
|
29408
|
+
if (buckets.has(group.id)) continue;
|
|
29409
|
+
const name = uniqueName(group.name, names);
|
|
29410
|
+
const id2 = takeId(group.id, name, "group", ids, true);
|
|
29411
|
+
const bucket = {
|
|
29412
|
+
requestlyId: group.id,
|
|
29413
|
+
id: id2,
|
|
29414
|
+
name,
|
|
29415
|
+
inactive: group.status === "Inactive",
|
|
29416
|
+
rules: []
|
|
29417
|
+
};
|
|
29418
|
+
buckets.set(group.id, bucket);
|
|
29419
|
+
ordered.push(bucket);
|
|
29420
|
+
}
|
|
29421
|
+
let ungrouped;
|
|
29422
|
+
const ensureUngrouped = () => {
|
|
29423
|
+
if (ungrouped !== void 0) return ungrouped;
|
|
29424
|
+
const name = uniqueName(UNGROUPED_NAME, names);
|
|
29425
|
+
const id2 = takeId("Ungrouped", name, "group", ids, true);
|
|
29426
|
+
ungrouped = {
|
|
29427
|
+
requestlyId: void 0,
|
|
29428
|
+
id: id2,
|
|
29429
|
+
name,
|
|
29430
|
+
inactive: false,
|
|
29431
|
+
rules: []
|
|
29432
|
+
};
|
|
29433
|
+
return ungrouped;
|
|
29434
|
+
};
|
|
29435
|
+
const rows = [];
|
|
29436
|
+
let priority = LIMITS.maxPriority;
|
|
29437
|
+
let assignedAtFloor = 0;
|
|
29438
|
+
let produced = 0;
|
|
29439
|
+
for (const rule of parsed.export.rules) {
|
|
29440
|
+
const placed = placeRule(rule, buckets, ensureUngrouped);
|
|
29441
|
+
const changes = [...placed.changes];
|
|
29442
|
+
const rogatioRuleIds = [];
|
|
29443
|
+
for (const draft of placed.drafts) {
|
|
29444
|
+
const labeled = draft.nameSuffix.length > 0 ? `${cleanLabel(rule.name, "Imported rule")} (${draft.nameSuffix})` : cleanLabel(rule.name, "Imported rule");
|
|
29445
|
+
const storedName = uniqueName(labeled, names);
|
|
29446
|
+
if (storedName !== labeled) {
|
|
29447
|
+
changes.push(
|
|
29448
|
+
`The name was already used and was renamed to "${storedName}".`
|
|
29449
|
+
);
|
|
29450
|
+
}
|
|
29451
|
+
const id2 = takeId(rule.id, storedName, "rule", ids, draft.keepId);
|
|
29452
|
+
if (draft.keepId && rule.id !== void 0 && id2 !== rule.id) {
|
|
29453
|
+
changes.push(
|
|
29454
|
+
isRogatioId(rule.id) ? `The Requestly id "${rule.id}" was already used and became "${id2}".` : `The Requestly id "${rule.id}" is not a Rogatio id and became "${id2}".`
|
|
29455
|
+
);
|
|
29456
|
+
}
|
|
29457
|
+
const next = priority;
|
|
29458
|
+
if (next === LIMITS.minPriority) assignedAtFloor += 1;
|
|
29459
|
+
else priority -= 1;
|
|
29460
|
+
produced += 1;
|
|
29461
|
+
rogatioRuleIds.push(id2);
|
|
29462
|
+
placed.bucket.rules.push({
|
|
29463
|
+
...draft.rule,
|
|
29464
|
+
id: id2,
|
|
29465
|
+
name: storedName,
|
|
29466
|
+
priority: next,
|
|
29467
|
+
resourceTypes: [...draft.rule.resourceTypes]
|
|
29468
|
+
});
|
|
29469
|
+
}
|
|
29470
|
+
if (placed.drafts.length === 0) {
|
|
29471
|
+
rows.push({
|
|
29472
|
+
...rule.id === void 0 ? {} : { requestlyId: rule.id },
|
|
29473
|
+
name: displayName(rule.name),
|
|
29474
|
+
ruleType: rule.ruleType.length > 0 ? rule.ruleType : "unknown",
|
|
29475
|
+
status: "skipped",
|
|
29476
|
+
reason: placed.reason ?? "The rule was not imported."
|
|
29477
|
+
});
|
|
29478
|
+
continue;
|
|
29479
|
+
}
|
|
29480
|
+
const uniqueChanges = dedupe2(changes);
|
|
29481
|
+
rows.push({
|
|
29482
|
+
...rule.id === void 0 ? {} : { requestlyId: rule.id },
|
|
29483
|
+
name: displayName(rule.name),
|
|
29484
|
+
ruleType: rule.ruleType.length > 0 ? rule.ruleType : "unknown",
|
|
29485
|
+
status: uniqueChanges.length === 0 ? "imported" : "changed",
|
|
29486
|
+
...uniqueChanges.length === 0 ? {} : { changes: uniqueChanges },
|
|
29487
|
+
rogatioRuleIds
|
|
29488
|
+
});
|
|
29489
|
+
}
|
|
29490
|
+
for (const record of parsed.export.unknown) {
|
|
29491
|
+
rows.push({
|
|
29492
|
+
name: record.name,
|
|
29493
|
+
ruleType: record.type,
|
|
29494
|
+
status: "skipped",
|
|
29495
|
+
reason: "The record is not a Requestly rule."
|
|
29496
|
+
});
|
|
29497
|
+
}
|
|
29498
|
+
if (ungrouped !== void 0 && ungrouped.rules.length > 0) {
|
|
29499
|
+
ordered.push(ungrouped);
|
|
29500
|
+
}
|
|
29501
|
+
const groups = ordered.map((bucket) => ({
|
|
29502
|
+
id: bucket.id,
|
|
29503
|
+
name: bucket.name,
|
|
29504
|
+
rules: bucket.rules
|
|
29505
|
+
}));
|
|
29506
|
+
const limited = limitError(groups);
|
|
29507
|
+
if (limited !== void 0) return { ok: false, error: limited };
|
|
29508
|
+
const notes = [];
|
|
29509
|
+
if (produced > 0) notes.push(PRIORITY_NOTE);
|
|
29510
|
+
if (assignedAtFloor > 1) notes.push(PRIORITY_FLOOR_NOTE);
|
|
29511
|
+
return {
|
|
29512
|
+
ok: true,
|
|
29513
|
+
project: {
|
|
29514
|
+
version: PROJECT_VERSION,
|
|
29515
|
+
name: "Imported from Requestly",
|
|
29516
|
+
description: "Imported from a Requestly export.",
|
|
29517
|
+
groups
|
|
29518
|
+
},
|
|
29519
|
+
report: summarize(rows, notes, parsed.export.unknown)
|
|
29520
|
+
};
|
|
29521
|
+
}
|
|
29522
|
+
function placeRule(rule, buckets, ensureUngrouped) {
|
|
29523
|
+
const mapped = mapRequestlyRule(rule.raw);
|
|
29524
|
+
const known = rule.groupId.length > 0 ? buckets.get(rule.groupId) : void 0;
|
|
29525
|
+
const missingGroup = rule.groupId.length > 0 && known === void 0;
|
|
29526
|
+
if (mapped.status === "skipped") {
|
|
29527
|
+
return {
|
|
29528
|
+
drafts: [],
|
|
29529
|
+
changes: [],
|
|
29530
|
+
reason: mapped.reason,
|
|
29531
|
+
bucket: known ?? emptyBucket()
|
|
29532
|
+
};
|
|
29533
|
+
}
|
|
29534
|
+
const bucket = known ?? ensureUngrouped();
|
|
29535
|
+
const changes = [...mapped.changes];
|
|
29536
|
+
if (ownString3(rule.raw, "status") === "Inactive" || bucket.inactive) {
|
|
29537
|
+
changes.push(INACTIVE_CHANGE);
|
|
29538
|
+
}
|
|
29539
|
+
const description = ownString3(rule.raw, "description") ?? "";
|
|
29540
|
+
if (description.trim().length > 0) changes.push(DESCRIPTION_CHANGE);
|
|
29541
|
+
if (missingGroup) changes.push(MISSING_GROUP_CHANGE);
|
|
29542
|
+
return { drafts: mapped.drafts, changes, bucket };
|
|
29543
|
+
}
|
|
29544
|
+
function summarize(rows, notes, unknown) {
|
|
29545
|
+
let imported = 0;
|
|
29546
|
+
let changed = 0;
|
|
29547
|
+
let skipped = 0;
|
|
29548
|
+
for (const row of rows) {
|
|
29549
|
+
if (row.status === "imported") imported += 1;
|
|
29550
|
+
else if (row.status === "changed") changed += 1;
|
|
29551
|
+
else skipped += 1;
|
|
29552
|
+
}
|
|
29553
|
+
return { imported, changed, skipped, rules: rows, notes, unknown };
|
|
29554
|
+
}
|
|
29555
|
+
function isRogatioId(value) {
|
|
29556
|
+
return value.length > 0 && value.length <= LIMITS.maxIdLength && ID_PATTERN3.test(value);
|
|
29557
|
+
}
|
|
29558
|
+
function takeId(preferred, name, kind, reserved, keep) {
|
|
29559
|
+
if (keep && preferred !== void 0 && isRogatioId(preferred) && !reserved.has(preferred)) {
|
|
29560
|
+
reserved.add(preferred);
|
|
29561
|
+
return preferred;
|
|
29562
|
+
}
|
|
29563
|
+
return deriveEntityId(name, kind, reserved);
|
|
29564
|
+
}
|
|
29565
|
+
function limitError(groups) {
|
|
29566
|
+
if (groups.length > LIMITS.maxGroups) {
|
|
29567
|
+
return `The import needs ${groups.length} groups, above the limit of ${LIMITS.maxGroups}.`;
|
|
29568
|
+
}
|
|
29569
|
+
let total = 0;
|
|
29570
|
+
for (const group of groups) {
|
|
29571
|
+
if (group.rules.length > LIMITS.maxRulesPerGroup) {
|
|
29572
|
+
return `Group "${group.name}" has ${group.rules.length} rules, above the limit of ${LIMITS.maxRulesPerGroup}.`;
|
|
29573
|
+
}
|
|
29574
|
+
total += group.rules.length;
|
|
29575
|
+
}
|
|
29576
|
+
if (total > LIMITS.maxRulesPerProject) {
|
|
29577
|
+
return `The import has ${total} rules, above the limit of ${LIMITS.maxRulesPerProject}.`;
|
|
29578
|
+
}
|
|
29579
|
+
return void 0;
|
|
29580
|
+
}
|
|
29581
|
+
function dedupe2(changes) {
|
|
29582
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29583
|
+
const result = [];
|
|
29584
|
+
for (const change of changes) {
|
|
29585
|
+
if (seen.has(change)) continue;
|
|
29586
|
+
seen.add(change);
|
|
29587
|
+
result.push(change);
|
|
29588
|
+
}
|
|
29589
|
+
return result;
|
|
29590
|
+
}
|
|
29591
|
+
function collectIdentity(project) {
|
|
29592
|
+
const ids = /* @__PURE__ */ new Set();
|
|
29593
|
+
const names = /* @__PURE__ */ new Set();
|
|
29594
|
+
for (const group of project.groups) {
|
|
29595
|
+
ids.add(group.id);
|
|
29596
|
+
names.add(normalizeNameKey(group.name));
|
|
29597
|
+
for (const rule of group.rules) {
|
|
29598
|
+
ids.add(rule.id);
|
|
29599
|
+
names.add(normalizeNameKey(rule.name));
|
|
29600
|
+
}
|
|
29601
|
+
}
|
|
29602
|
+
return { ids, names };
|
|
29603
|
+
}
|
|
29604
|
+
function mergeProjects(existing, imported) {
|
|
29605
|
+
const current = cloneProject(existing);
|
|
29606
|
+
if (current === void 0) {
|
|
29607
|
+
return {
|
|
29608
|
+
ok: false,
|
|
29609
|
+
error: "The existing project is not a version-2 Rogatio project."
|
|
29610
|
+
};
|
|
29611
|
+
}
|
|
29612
|
+
const incoming = cloneProject(imported);
|
|
29613
|
+
if (incoming === void 0) {
|
|
29614
|
+
return { ok: false, error: "The imported project is not JSON data." };
|
|
29615
|
+
}
|
|
29616
|
+
const { ids, names } = collectIdentity(current);
|
|
29617
|
+
const renamed = [];
|
|
29618
|
+
const appended = [];
|
|
29619
|
+
for (const group of incoming.groups) {
|
|
29620
|
+
const name = uniqueName(group.name, names);
|
|
29621
|
+
if (name !== group.name) {
|
|
29622
|
+
renamed.push(
|
|
29623
|
+
`Imported group name "${group.name}" was already used and became "${name}".`
|
|
29624
|
+
);
|
|
29625
|
+
}
|
|
29626
|
+
const id2 = retakeId(group.id, name, "group", ids, renamed, "group");
|
|
29627
|
+
const rules = [];
|
|
29628
|
+
for (const rule of group.rules) {
|
|
29629
|
+
const ruleName = uniqueName(rule.name, names);
|
|
29630
|
+
if (ruleName !== rule.name) {
|
|
29631
|
+
renamed.push(
|
|
29632
|
+
`Imported rule name "${rule.name}" was already used and became "${ruleName}".`
|
|
29633
|
+
);
|
|
29634
|
+
}
|
|
29635
|
+
const ruleId = retakeId(rule.id, ruleName, "rule", ids, renamed, "rule");
|
|
29636
|
+
rules.push({ ...rule, id: ruleId, name: ruleName });
|
|
29637
|
+
}
|
|
29638
|
+
appended.push({ id: id2, name, rules });
|
|
29639
|
+
}
|
|
29640
|
+
const groups = [...current.groups, ...appended];
|
|
29641
|
+
const limited = limitError(groups);
|
|
29642
|
+
if (limited !== void 0) return { ok: false, error: limited };
|
|
29643
|
+
const project = {
|
|
29644
|
+
version: PROJECT_VERSION,
|
|
29645
|
+
name: current.name,
|
|
29646
|
+
...current.description === void 0 ? {} : { description: current.description },
|
|
29647
|
+
groups,
|
|
29648
|
+
...current.requestBodyPolicy === void 0 ? {} : { requestBodyPolicy: current.requestBodyPolicy }
|
|
29649
|
+
};
|
|
29650
|
+
return { ok: true, project, renamed };
|
|
29651
|
+
}
|
|
29652
|
+
function retakeId(preferred, name, kind, reserved, renamed, label2) {
|
|
29653
|
+
if (isRogatioId(preferred) && !reserved.has(preferred)) {
|
|
29654
|
+
reserved.add(preferred);
|
|
29655
|
+
return preferred;
|
|
29656
|
+
}
|
|
29657
|
+
const next = deriveEntityId(name, kind, reserved);
|
|
29658
|
+
renamed.push(
|
|
29659
|
+
isRogatioId(preferred) ? `Imported ${label2} id "${preferred}" was already used and became "${next}".` : `Imported ${label2} id "${preferred}" is not a Rogatio id and became "${next}".`
|
|
29660
|
+
);
|
|
29661
|
+
return next;
|
|
29662
|
+
}
|
|
29663
|
+
function cloneProject(value) {
|
|
29664
|
+
let cloned;
|
|
29665
|
+
try {
|
|
29666
|
+
cloned = JSON.parse(JSON.stringify(value));
|
|
29667
|
+
} catch {
|
|
29668
|
+
return void 0;
|
|
29669
|
+
}
|
|
29670
|
+
if (cloned === null || typeof cloned !== "object" || Array.isArray(cloned)) {
|
|
29671
|
+
return void 0;
|
|
29672
|
+
}
|
|
29673
|
+
const record = cloned;
|
|
29674
|
+
if (record.version !== PROJECT_VERSION || typeof record.name !== "string") {
|
|
29675
|
+
return void 0;
|
|
29676
|
+
}
|
|
29677
|
+
if (!Array.isArray(record.groups)) return void 0;
|
|
29678
|
+
return record;
|
|
29679
|
+
}
|
|
29680
|
+
|
|
29681
|
+
// packages/cli/src/commands/diagnose.ts
|
|
29682
|
+
function diagnoseProject(projectData) {
|
|
29683
|
+
const diagnostics = [];
|
|
29684
|
+
const schemaResult = validateProjectDetailed(projectData);
|
|
29685
|
+
if (!schemaResult.valid) {
|
|
29686
|
+
for (const error of schemaResult.errors) {
|
|
29687
|
+
diagnostics.push({
|
|
29688
|
+
code: `schema.${error.keyword}`,
|
|
29689
|
+
severity: "error",
|
|
29690
|
+
path: error.instancePath || "/",
|
|
29691
|
+
message: error.message,
|
|
29692
|
+
params: error.params
|
|
29693
|
+
});
|
|
29694
|
+
}
|
|
29695
|
+
return { diagnostics };
|
|
29696
|
+
}
|
|
29697
|
+
const compileResult = compileProject(schemaResult.data);
|
|
29698
|
+
if (!compileResult.ok) {
|
|
29699
|
+
for (const diag of compileResult.diagnostics) {
|
|
29700
|
+
diagnostics.push({
|
|
29701
|
+
code: diag.code,
|
|
29702
|
+
severity: diag.severity,
|
|
29703
|
+
path: diag.path,
|
|
29704
|
+
message: diag.message,
|
|
29705
|
+
params: { ...diag.params }
|
|
29706
|
+
});
|
|
29707
|
+
}
|
|
29708
|
+
return { diagnostics };
|
|
29709
|
+
}
|
|
29710
|
+
return { diagnostics, project: schemaResult.data };
|
|
29711
|
+
}
|
|
29712
|
+
function formatDiagnostics(diagnostics) {
|
|
29713
|
+
return diagnostics.map((diag) => `${diag.path}: ${diag.message} (${diag.code})`).join("\n");
|
|
29714
|
+
}
|
|
29715
|
+
|
|
29716
|
+
// packages/cli/src/commands/import.ts
|
|
29717
|
+
async function importCommand(args) {
|
|
29718
|
+
const parsed = parseImportArgs(args);
|
|
29719
|
+
if (!parsed.ok) {
|
|
29720
|
+
console.error(parsed.error);
|
|
29721
|
+
return 2;
|
|
29722
|
+
}
|
|
29723
|
+
const flags = parsed.flags;
|
|
29724
|
+
let exportData;
|
|
29725
|
+
try {
|
|
29726
|
+
exportData = JSON.parse(await readFile6(flags.sourcePath, "utf8"));
|
|
29727
|
+
} catch (error) {
|
|
29728
|
+
console.error(`Error: ${errorMessage(error)}`);
|
|
29729
|
+
return 2;
|
|
29730
|
+
}
|
|
29731
|
+
const imported = importRequestlyExport(exportData);
|
|
29732
|
+
if (!imported.ok) {
|
|
29733
|
+
console.error(`Error: ${imported.error}`);
|
|
29734
|
+
return 2;
|
|
29735
|
+
}
|
|
29736
|
+
let project = imported.project;
|
|
29737
|
+
let renamed = [];
|
|
29738
|
+
if (flags.merge) {
|
|
29739
|
+
if (!await pathExists2(flags.outputPath)) {
|
|
29740
|
+
console.error(`Error: No project to merge at ${flags.outputPath}`);
|
|
29741
|
+
return 2;
|
|
29742
|
+
}
|
|
29743
|
+
let existing;
|
|
29744
|
+
try {
|
|
29745
|
+
existing = JSON.parse(await readFile6(flags.outputPath, "utf8"));
|
|
29746
|
+
} catch (error) {
|
|
29747
|
+
console.error(`Error: ${errorMessage(error)}`);
|
|
29748
|
+
return 2;
|
|
29749
|
+
}
|
|
29750
|
+
const existingDiagnosis = diagnoseProject(existing);
|
|
29751
|
+
if (existingDiagnosis.diagnostics.length > 0 || existingDiagnosis.project === void 0) {
|
|
29752
|
+
console.error(formatDiagnostics(existingDiagnosis.diagnostics));
|
|
29753
|
+
return 1;
|
|
29754
|
+
}
|
|
29755
|
+
const merged = mergeProjects(existingDiagnosis.project, project);
|
|
29756
|
+
if (!merged.ok) {
|
|
29757
|
+
console.error(`Error: ${merged.error}`);
|
|
29758
|
+
return 2;
|
|
29759
|
+
}
|
|
29760
|
+
project = merged.project;
|
|
29761
|
+
renamed = merged.renamed;
|
|
29762
|
+
} else if (await pathExists2(flags.outputPath)) {
|
|
29763
|
+
console.error(
|
|
29764
|
+
`Error: ${flags.outputPath} already exists. Pass --merge to append imported groups, or choose another --out path.`
|
|
29765
|
+
);
|
|
29766
|
+
return 2;
|
|
29767
|
+
}
|
|
29768
|
+
const diagnostics = diagnoseProject(project).diagnostics;
|
|
29769
|
+
if (diagnostics.length > 0) {
|
|
29770
|
+
console.error(formatDiagnostics(diagnostics));
|
|
29771
|
+
return 1;
|
|
29772
|
+
}
|
|
29773
|
+
try {
|
|
29774
|
+
await writeProject(flags.outputPath, project);
|
|
29775
|
+
} catch (error) {
|
|
29776
|
+
console.error(`Error: ${errorMessage(error)}`);
|
|
29777
|
+
return 2;
|
|
29778
|
+
}
|
|
29779
|
+
if (flags.json) {
|
|
29780
|
+
console.log(
|
|
29781
|
+
`${JSON.stringify(jsonReport(imported.report, flags.outputPath, renamed), null, 2)}
|
|
29782
|
+
`
|
|
29783
|
+
);
|
|
29784
|
+
} else {
|
|
29785
|
+
console.log(formatReport(imported.report, flags.outputPath, renamed));
|
|
29786
|
+
}
|
|
29787
|
+
return 0;
|
|
29788
|
+
}
|
|
29789
|
+
function jsonReport(report, outputPath, renamed) {
|
|
29790
|
+
return { ...report, renamed, outputPath };
|
|
29791
|
+
}
|
|
29792
|
+
function formatReport(report, outputPath, renamed) {
|
|
29793
|
+
const lines = [
|
|
29794
|
+
`Imported ${report.imported}, changed ${report.changed}, skipped ${report.skipped}.`
|
|
29795
|
+
];
|
|
29796
|
+
for (const note of report.notes) lines.push(note);
|
|
29797
|
+
const changed = report.rules.filter((row) => row.status === "changed");
|
|
29798
|
+
const skipped = report.rules.filter((row) => row.status === "skipped");
|
|
29799
|
+
if (changed.length > 0) {
|
|
29800
|
+
lines.push("", "Changed:");
|
|
29801
|
+
for (const row of changed) {
|
|
29802
|
+
lines.push(` ${row.name} (${row.ruleType})`);
|
|
29803
|
+
for (const change of row.changes ?? []) lines.push(` - ${change}`);
|
|
29804
|
+
}
|
|
29805
|
+
}
|
|
29806
|
+
if (skipped.length > 0) {
|
|
29807
|
+
lines.push("", "Skipped:");
|
|
29808
|
+
for (const row of skipped) {
|
|
29809
|
+
lines.push(` ${row.name} (${row.ruleType}): ${row.reason ?? ""}`);
|
|
29810
|
+
}
|
|
29811
|
+
}
|
|
29812
|
+
if (renamed.length > 0) {
|
|
29813
|
+
lines.push("", "Renamed:");
|
|
29814
|
+
for (const line of renamed) lines.push(` ${line}`);
|
|
29815
|
+
}
|
|
29816
|
+
lines.push("", `Wrote ${outputPath}`);
|
|
29817
|
+
return lines.join("\n");
|
|
29818
|
+
}
|
|
29819
|
+
function parseImportArgs(args) {
|
|
29820
|
+
const positionals = [];
|
|
29821
|
+
let outputPath;
|
|
29822
|
+
let merge = false;
|
|
29823
|
+
let json = false;
|
|
29824
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
29825
|
+
const arg = args[index] ?? "";
|
|
29826
|
+
if (arg === "--merge") {
|
|
29827
|
+
merge = true;
|
|
29828
|
+
continue;
|
|
29829
|
+
}
|
|
29830
|
+
if (arg === "--json") {
|
|
29831
|
+
json = true;
|
|
29832
|
+
continue;
|
|
29833
|
+
}
|
|
29834
|
+
if (arg === "--out") {
|
|
29835
|
+
const value = args[index + 1];
|
|
29836
|
+
if (value === void 0 || value.startsWith("-")) {
|
|
29837
|
+
return { ok: false, error: "Error: --out requires a path" };
|
|
29838
|
+
}
|
|
29839
|
+
outputPath = resolve5(value);
|
|
29840
|
+
index += 1;
|
|
29841
|
+
continue;
|
|
29842
|
+
}
|
|
29843
|
+
if (arg.startsWith("-")) {
|
|
29844
|
+
return { ok: false, error: `Error: Unknown option: ${arg}` };
|
|
29845
|
+
}
|
|
29846
|
+
positionals.push(arg);
|
|
29847
|
+
}
|
|
29848
|
+
if (positionals[0] !== "requestly") {
|
|
29849
|
+
return {
|
|
29850
|
+
ok: false,
|
|
29851
|
+
error: "Error: Usage: rogatio import requestly <export.json> [--out <path>] [--merge] [--json]"
|
|
29852
|
+
};
|
|
29853
|
+
}
|
|
29854
|
+
const source = positionals[1];
|
|
29855
|
+
if (source === void 0) {
|
|
29856
|
+
return { ok: false, error: "Error: Missing Requestly export path" };
|
|
29857
|
+
}
|
|
29858
|
+
if (positionals.length > 2) {
|
|
29859
|
+
return { ok: false, error: "Error: Too many arguments" };
|
|
29860
|
+
}
|
|
29861
|
+
return {
|
|
29862
|
+
ok: true,
|
|
29863
|
+
flags: {
|
|
29864
|
+
sourcePath: resolve5(source),
|
|
29865
|
+
outputPath: outputPath ?? resolve5(process.cwd(), ".rogatio.json"),
|
|
29866
|
+
merge,
|
|
29867
|
+
json
|
|
29868
|
+
}
|
|
29869
|
+
};
|
|
29870
|
+
}
|
|
29871
|
+
async function pathExists2(path2) {
|
|
29872
|
+
try {
|
|
29873
|
+
await access2(path2);
|
|
29874
|
+
return true;
|
|
29875
|
+
} catch {
|
|
29876
|
+
return false;
|
|
29877
|
+
}
|
|
29878
|
+
}
|
|
29879
|
+
function errorMessage(error) {
|
|
29880
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
29881
|
+
}
|
|
29882
|
+
|
|
29883
|
+
// packages/cli/src/commands/runtime.ts
|
|
29884
|
+
import { chmod, mkdir as mkdir4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
|
|
29885
|
+
import { dirname as dirname7, join as join7, resolve as resolve6 } from "node:path";
|
|
29886
|
+
function toMatcherOperations2(operations) {
|
|
29887
|
+
return operations.map(
|
|
29888
|
+
({ groupId, ruleId, name, matcher, redactSensitiveInLogs }) => ({
|
|
29889
|
+
kind: "matcher",
|
|
29890
|
+
groupId,
|
|
29891
|
+
ruleId,
|
|
29892
|
+
name,
|
|
29893
|
+
matcher,
|
|
29894
|
+
redactSensitiveInLogs
|
|
29895
|
+
})
|
|
29896
|
+
);
|
|
29897
|
+
}
|
|
29898
|
+
function generateHostWrapper(rogatioCommand, logPath) {
|
|
29899
|
+
const parts = rogatioCommand.split(" ");
|
|
29900
|
+
const nodeBin = parts[0] ?? "node";
|
|
29901
|
+
const cliScript = parts.slice(1).join(" ");
|
|
29902
|
+
return `#!/bin/sh
|
|
29903
|
+
# Rogatio native messaging host wrapper \u2014 generated by \`rogatio runtime install\`
|
|
29904
|
+
# Chrome launches this script when the extension connects via connectNative().
|
|
29905
|
+
# The extension sends the project data as the first envelope after launch.
|
|
29906
|
+
# stderr is redirected to a log file for debugging.
|
|
29907
|
+
exec "${nodeBin}" "${cliScript}" runtime host 2>>"${logPath}"
|
|
29908
|
+
`;
|
|
29909
|
+
}
|
|
29910
|
+
async function writeHostWrapper(hostPath, installRoot) {
|
|
29911
|
+
await mkdir4(installRoot, { recursive: true });
|
|
29912
|
+
const logPath = join7(installRoot, "runtime-host.log");
|
|
29913
|
+
const script = generateHostWrapper(
|
|
29914
|
+
`${process.execPath} ${process.argv[1] ?? "rogatio"}`,
|
|
29915
|
+
logPath
|
|
29916
|
+
);
|
|
29917
|
+
await writeFile4(hostPath, script, "utf8");
|
|
29918
|
+
await chmod(hostPath, 493);
|
|
29919
|
+
}
|
|
29920
|
+
function makeTrustController() {
|
|
29921
|
+
const platform = process.platform;
|
|
29922
|
+
const adapter = selectTrustPlatformAdapter(platform);
|
|
29923
|
+
const installRoot = defaultTrustInstallRoot(platform);
|
|
29924
|
+
return createRequestBodyTrustController({
|
|
29925
|
+
platform,
|
|
29926
|
+
installRoot,
|
|
29927
|
+
hostPath: join7(installRoot, "runtime-host"),
|
|
29928
|
+
hostName: "com.rogatio.runtime",
|
|
29929
|
+
allowedOrigins: [],
|
|
29930
|
+
manifestDir: adapter.defaultManifestDir(),
|
|
29931
|
+
detectCapabilities: () => adapter.detect(),
|
|
29932
|
+
caTrustInstaller: (cert) => adapter.caTrustInstaller(cert),
|
|
29933
|
+
caTrustRemover: () => adapter.caTrustRemover()
|
|
29934
|
+
});
|
|
29935
|
+
}
|
|
29936
|
+
function reportTrust(subcommand, result, okMessage) {
|
|
29937
|
+
if (result.ok) {
|
|
29938
|
+
console.log(okMessage);
|
|
29939
|
+
return 0;
|
|
29940
|
+
}
|
|
29941
|
+
if (result.state === "unsupported") {
|
|
29942
|
+
const reasons = result.reasons ?? ["unknown"];
|
|
29943
|
+
console.error(`trust unsupported: ${reasons.join(", ")}`);
|
|
29944
|
+
const hints = {
|
|
29945
|
+
"tooling-missing": "Hint: install required tooling (macOS: security; Linux: update-ca-certificates; Windows: certutil is built-in)",
|
|
29946
|
+
"manifest-dir-unwritable": "Hint: check permissions on Chrome NativeMessagingHosts directory or run with appropriate access",
|
|
29947
|
+
"keychain-unwritable": "Hint: macOS login keychain not writable; try running with appropriate keychain access or use sudo for system keychain",
|
|
29948
|
+
"ca-store-unwritable": "Hint: /usr/local/share/ca-certificates not writable; check directory permissions",
|
|
29949
|
+
"elevation-required": "Hint: device-local CA trust requires root (Linux: sudo, macOS: keychain password, Windows: Administrator). Re-run with elevated privileges"
|
|
29950
|
+
};
|
|
29951
|
+
for (const reason of reasons) {
|
|
29952
|
+
const hint = hints[reason];
|
|
29953
|
+
if (hint) {
|
|
29954
|
+
console.error(hint);
|
|
29955
|
+
}
|
|
29956
|
+
}
|
|
29957
|
+
return 1;
|
|
29958
|
+
}
|
|
29959
|
+
console.error(
|
|
29960
|
+
`runtime ${subcommand} failed: ${(result.reasons ?? ["unknown"]).join(", ")}`
|
|
29961
|
+
);
|
|
29962
|
+
return 1;
|
|
29963
|
+
}
|
|
29964
|
+
async function trustRuntimeCommand(args) {
|
|
29965
|
+
const subcommand = args[0];
|
|
29966
|
+
let extensionId;
|
|
29967
|
+
if (subcommand === "install") {
|
|
29968
|
+
const extIdIndex = args.indexOf("--extension-id");
|
|
29969
|
+
if (extIdIndex === -1 || extIdIndex + 1 >= args.length) {
|
|
29970
|
+
console.error("Error: --extension-id is required for install command");
|
|
29971
|
+
showRuntimeHelp();
|
|
29972
|
+
return 2;
|
|
29973
|
+
}
|
|
29974
|
+
extensionId = args[extIdIndex + 1];
|
|
29975
|
+
if (!/^[a-p]{32}$/.test(extensionId)) {
|
|
29976
|
+
console.error(
|
|
29977
|
+
"Error: --extension-id must be exactly 32 lowercase characters from a through p"
|
|
29978
|
+
);
|
|
29979
|
+
return 2;
|
|
29980
|
+
}
|
|
29981
|
+
}
|
|
29982
|
+
const controller = makeTrustController();
|
|
29983
|
+
const installRoot = defaultTrustInstallRoot(process.platform);
|
|
29984
|
+
const hostPath = join7(installRoot, "runtime-host");
|
|
29985
|
+
switch (subcommand) {
|
|
29986
|
+
case "install": {
|
|
29987
|
+
const installResult = await controller.install(extensionId ?? "");
|
|
29988
|
+
if (installResult.ok) {
|
|
29989
|
+
try {
|
|
29990
|
+
await writeHostWrapper(hostPath, installRoot);
|
|
29991
|
+
} catch (error) {
|
|
29992
|
+
console.error(
|
|
29993
|
+
`Warning: manifest and CA trusted, but failed to create runtime-host wrapper: ${error instanceof Error ? error.message : String(error)}`
|
|
29994
|
+
);
|
|
29995
|
+
}
|
|
29996
|
+
}
|
|
29997
|
+
return reportTrust(
|
|
29998
|
+
"install",
|
|
29999
|
+
installResult,
|
|
30000
|
+
"runtime install complete: manifest + device-local CA trusted + runtime-host wrapper created\nNote: reload the extension at chrome://extensions for the native host to be detected."
|
|
30001
|
+
);
|
|
30002
|
+
}
|
|
30003
|
+
case "uninstall": {
|
|
30004
|
+
const uninstallResult = await controller.uninstall();
|
|
30005
|
+
try {
|
|
30006
|
+
await rm2(hostPath, { force: true });
|
|
30007
|
+
} catch {
|
|
30008
|
+
}
|
|
30009
|
+
try {
|
|
30010
|
+
await rm2(join7(installRoot, "runtime-host.log"), { force: true });
|
|
30011
|
+
} catch {
|
|
30012
|
+
}
|
|
30013
|
+
return reportTrust(
|
|
30014
|
+
"uninstall",
|
|
30015
|
+
uninstallResult,
|
|
30016
|
+
"runtime uninstall complete: manifest + device-local CA removed"
|
|
30017
|
+
);
|
|
30018
|
+
}
|
|
30019
|
+
case "verify": {
|
|
30020
|
+
const result = await controller.verify();
|
|
30021
|
+
if (result.ok) {
|
|
30022
|
+
console.log(
|
|
30023
|
+
"runtime verification passed: manifest, binary, and CA trust all OK"
|
|
30024
|
+
);
|
|
30025
|
+
return 0;
|
|
30026
|
+
}
|
|
30027
|
+
console.error("runtime verification failed:");
|
|
30028
|
+
if (!result.manifestExists) console.error(" manifest not found");
|
|
30029
|
+
if (result.manifestExists && !result.manifestValid)
|
|
30030
|
+
console.error(" manifest invalid");
|
|
30031
|
+
if (!result.binaryExists)
|
|
30032
|
+
console.error(" runtime-host binary not found");
|
|
30033
|
+
if (result.binaryExists && !result.binaryExecutable)
|
|
30034
|
+
console.error(" runtime-host binary not executable");
|
|
30035
|
+
if (result.allowedOriginsCount === 0)
|
|
30036
|
+
console.error(" no allowed_origins in manifest");
|
|
30037
|
+
if (!result.caTrusted) console.error(" device-local CA not trusted");
|
|
30038
|
+
for (const reason of result.reasons) {
|
|
30039
|
+
if (reason === "manifest-not-found")
|
|
30040
|
+
console.error(
|
|
30041
|
+
" Hint: run 'rogatio runtime install --extension-id <id>' to register the host manifest"
|
|
30042
|
+
);
|
|
30043
|
+
if (reason === "binary-not-found")
|
|
30044
|
+
console.error(
|
|
30045
|
+
` Hint: ensure the runtime-host binary exists at the path specified in the manifest`
|
|
30046
|
+
);
|
|
30047
|
+
if (reason === "binary-not-executable")
|
|
30048
|
+
console.error(
|
|
30049
|
+
" Hint: ensure the runtime-host binary has execute permission (chmod +x)"
|
|
30050
|
+
);
|
|
30051
|
+
if (reason === "no-allowed-origins")
|
|
30052
|
+
console.error(
|
|
30053
|
+
" Hint: re-run 'rogatio runtime install --extension-id <id>' to pin your extension ID"
|
|
30054
|
+
);
|
|
30055
|
+
if (reason === "ca-not-trusted")
|
|
30056
|
+
console.error(
|
|
30057
|
+
" Hint: re-run 'rogatio runtime install --extension-id <id>' with elevated privileges to trust the CA"
|
|
30058
|
+
);
|
|
30059
|
+
}
|
|
30060
|
+
return 1;
|
|
30061
|
+
}
|
|
30062
|
+
default:
|
|
30063
|
+
console.error(`Error: unknown runtime subcommand: ${subcommand ?? ""}`);
|
|
30064
|
+
showRuntimeHelp();
|
|
30065
|
+
return 2;
|
|
30066
|
+
}
|
|
30067
|
+
}
|
|
30068
|
+
async function runtimeHostCommand(args, options = {}) {
|
|
30069
|
+
let root;
|
|
30070
|
+
let mockPort;
|
|
30071
|
+
const positional = [];
|
|
30072
|
+
let argumentError;
|
|
27956
30073
|
for (let index = 0; index < args.length; index += 1) {
|
|
27957
30074
|
const arg = args[index];
|
|
27958
30075
|
if (arg === "--root" && index + 1 < args.length) {
|
|
27959
|
-
root =
|
|
30076
|
+
root = resolve6(args[++index]);
|
|
27960
30077
|
} else if (arg === "--root") {
|
|
27961
30078
|
argumentError = "--root requires a value";
|
|
27962
30079
|
} else if (arg === "--mock-port" && index + 1 < args.length) {
|
|
@@ -28004,7 +30121,7 @@ async function runtimeHostCommand(args, options = {}) {
|
|
|
28004
30121
|
filePath = "<stdin>";
|
|
28005
30122
|
projectData = JSON.parse(chunks.join(""));
|
|
28006
30123
|
} else {
|
|
28007
|
-
filePath =
|
|
30124
|
+
filePath = resolve6(inputPath);
|
|
28008
30125
|
projectData = await storage.get(filePath);
|
|
28009
30126
|
}
|
|
28010
30127
|
} catch (error) {
|
|
@@ -28073,8 +30190,8 @@ async function runRuntimeHostEntry() {
|
|
|
28073
30190
|
}
|
|
28074
30191
|
|
|
28075
30192
|
// packages/cli/src/commands/test.ts
|
|
28076
|
-
import { readFile as
|
|
28077
|
-
import { resolve as
|
|
30193
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
30194
|
+
import { resolve as resolve7 } from "node:path";
|
|
28078
30195
|
function usageError(message) {
|
|
28079
30196
|
return `Error: ${message}
|
|
28080
30197
|
`;
|
|
@@ -28236,7 +30353,7 @@ function testCommandNeedsStdin(args) {
|
|
|
28236
30353
|
return !hasUrlSource && positionalUrls.length === 0;
|
|
28237
30354
|
}
|
|
28238
30355
|
async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
|
|
28239
|
-
let filePath =
|
|
30356
|
+
let filePath = resolve7(process.cwd(), ".rogatio.json");
|
|
28240
30357
|
let jsonMode = false;
|
|
28241
30358
|
let maxCases;
|
|
28242
30359
|
const urlCases = [];
|
|
@@ -28296,12 +30413,12 @@ async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
|
|
|
28296
30413
|
}
|
|
28297
30414
|
filePath = "<stdin>";
|
|
28298
30415
|
} else if (inputPath) {
|
|
28299
|
-
filePath =
|
|
30416
|
+
filePath = resolve7(inputPath);
|
|
28300
30417
|
}
|
|
28301
30418
|
for (const url of positionalUrls) urlCases.push({ url });
|
|
28302
30419
|
if (urlsFile) {
|
|
28303
30420
|
try {
|
|
28304
|
-
const content = urlsFile === "-" ? stdinInput ?? "" : await
|
|
30421
|
+
const content = urlsFile === "-" ? stdinInput ?? "" : await readFile7(urlsFile, "utf8");
|
|
28305
30422
|
const parsed = JSON.parse(content);
|
|
28306
30423
|
if (!Array.isArray(parsed)) {
|
|
28307
30424
|
const output = usageError(`${urlsFile} must contain a JSON array`);
|
|
@@ -28452,7 +30569,7 @@ async function testCommand(args, stdinInput, captureOutput = false, options = {}
|
|
|
28452
30569
|
}
|
|
28453
30570
|
|
|
28454
30571
|
// packages/cli/src/commands/verify.ts
|
|
28455
|
-
import { resolve as
|
|
30572
|
+
import { resolve as resolve8 } from "node:path";
|
|
28456
30573
|
async function verifyCommandImpl(args, stdinInput, captureOutput, options = {}) {
|
|
28457
30574
|
let filePath;
|
|
28458
30575
|
let jsonOutput2 = false;
|
|
@@ -28479,9 +30596,9 @@ async function verifyCommandImpl(args, stdinInput, captureOutput, options = {})
|
|
|
28479
30596
|
}
|
|
28480
30597
|
filePath = "<stdin>";
|
|
28481
30598
|
} else if (inputPath) {
|
|
28482
|
-
filePath =
|
|
30599
|
+
filePath = resolve8(inputPath);
|
|
28483
30600
|
} else {
|
|
28484
|
-
filePath =
|
|
30601
|
+
filePath = resolve8(process.cwd(), ".rogatio.json");
|
|
28485
30602
|
}
|
|
28486
30603
|
let projectData;
|
|
28487
30604
|
try {
|
|
@@ -28499,32 +30616,7 @@ async function verifyCommandImpl(args, stdinInput, captureOutput, options = {})
|
|
|
28499
30616
|
console.error(output2.trim());
|
|
28500
30617
|
return 2;
|
|
28501
30618
|
}
|
|
28502
|
-
const
|
|
28503
|
-
const diagnostics = [];
|
|
28504
|
-
if (!schemaResult.valid) {
|
|
28505
|
-
for (const error of schemaResult.errors) {
|
|
28506
|
-
diagnostics.push({
|
|
28507
|
-
code: `schema.${error.keyword}`,
|
|
28508
|
-
severity: "error",
|
|
28509
|
-
path: error.instancePath || "/",
|
|
28510
|
-
message: error.message,
|
|
28511
|
-
params: error.params
|
|
28512
|
-
});
|
|
28513
|
-
}
|
|
28514
|
-
} else {
|
|
28515
|
-
const compileResult = compileProject(schemaResult.data);
|
|
28516
|
-
if (!compileResult.ok) {
|
|
28517
|
-
for (const diag of compileResult.diagnostics) {
|
|
28518
|
-
diagnostics.push({
|
|
28519
|
-
code: diag.code,
|
|
28520
|
-
severity: diag.severity,
|
|
28521
|
-
path: diag.path,
|
|
28522
|
-
message: diag.message,
|
|
28523
|
-
params: diag.params
|
|
28524
|
-
});
|
|
28525
|
-
}
|
|
28526
|
-
}
|
|
28527
|
-
}
|
|
30619
|
+
const diagnostics = diagnoseProject(projectData).diagnostics;
|
|
28528
30620
|
let output = "";
|
|
28529
30621
|
if (jsonOutput2) {
|
|
28530
30622
|
output = `${JSON.stringify(diagnostics, null, 2)}
|
|
@@ -28550,7 +30642,7 @@ async function verifyCommand(args, stdinInput, captureOutput = false, options =
|
|
|
28550
30642
|
|
|
28551
30643
|
// packages/cli/src/index.ts
|
|
28552
30644
|
var __dirname = dirname8(fileURLToPath2(import.meta.url));
|
|
28553
|
-
var packageJsonPath =
|
|
30645
|
+
var packageJsonPath = resolve9(
|
|
28554
30646
|
__dirname,
|
|
28555
30647
|
isDistBuild(__dirname) ? "../../package.json" : "../package.json"
|
|
28556
30648
|
);
|
|
@@ -28577,6 +30669,8 @@ async function cli(args = process.argv.slice(2)) {
|
|
|
28577
30669
|
return handleRuntime(commandArgs);
|
|
28578
30670
|
case "ai":
|
|
28579
30671
|
return handleAI(commandArgs);
|
|
30672
|
+
case "import":
|
|
30673
|
+
return handleImport(commandArgs);
|
|
28580
30674
|
case "--help":
|
|
28581
30675
|
case "-h":
|
|
28582
30676
|
return showHelp();
|
|
@@ -28621,6 +30715,13 @@ async function handleAI(args) {
|
|
|
28621
30715
|
}
|
|
28622
30716
|
return await aiCommand(args);
|
|
28623
30717
|
}
|
|
30718
|
+
async function handleImport(args) {
|
|
30719
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
30720
|
+
showImportHelp();
|
|
30721
|
+
return 0;
|
|
30722
|
+
}
|
|
30723
|
+
return importCommand(args);
|
|
30724
|
+
}
|
|
28624
30725
|
async function handleTest(args) {
|
|
28625
30726
|
if (args.includes("--help") || args.includes("-h")) {
|
|
28626
30727
|
showTestHelp();
|
|
@@ -28653,6 +30754,7 @@ Commands:
|
|
|
28653
30754
|
edit [path] Launch browser editor for .rogatio.json
|
|
28654
30755
|
test [path] [url...] Run offline dry-run tests against .rogatio.json
|
|
28655
30756
|
verify [path] Validate .rogatio.json file
|
|
30757
|
+
import requestly <export.json> Migrate a Requestly export into .rogatio.json
|
|
28656
30758
|
runtime <install|uninstall|verify|host> Native messaging runtime control
|
|
28657
30759
|
runtime host [path] Run the native-messaging runtime host
|
|
28658
30760
|
ai <setup|ls|show|delete|test> AI provider configuration
|
|
@@ -28664,7 +30766,7 @@ Global Options:
|
|
|
28664
30766
|
Run 'rogatio <command> --help' for command-specific help.`);
|
|
28665
30767
|
return 0;
|
|
28666
30768
|
}
|
|
28667
|
-
if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath2(import.meta.url)) === realpathSync.native(
|
|
30769
|
+
if (process.argv[1] !== void 0 && realpathSync.native(fileURLToPath2(import.meta.url)) === realpathSync.native(resolve9(process.argv[1]))) {
|
|
28668
30770
|
if (basename3(process.argv[1]) === "runtime-host") {
|
|
28669
30771
|
runRuntimeHostEntry().catch((err) => {
|
|
28670
30772
|
console.error(err);
|