@depup/vercel 50.32.5-depup.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/LICENSE +202 -0
- package/README.md +38 -0
- package/changes.json +38 -0
- package/dist/chunks/chunk-2DLBVZWU.js +197 -0
- package/dist/chunks/chunk-2HSQ7YUK.js +93 -0
- package/dist/chunks/chunk-2IQTNMUG.js +86 -0
- package/dist/chunks/chunk-3FRG2XGZ.js +466 -0
- package/dist/chunks/chunk-3KMKI2FP.js +34 -0
- package/dist/chunks/chunk-3XFFP2BA.js +110 -0
- package/dist/chunks/chunk-4S3Y3ATR.js +5383 -0
- package/dist/chunks/chunk-7EHTK7LP.js +359 -0
- package/dist/chunks/chunk-7YHZDJ4G.js +116 -0
- package/dist/chunks/chunk-A3NYPUKZ.js +17 -0
- package/dist/chunks/chunk-AA7QEJFB.js +5204 -0
- package/dist/chunks/chunk-AHU7WNL2.js +24 -0
- package/dist/chunks/chunk-AKQZ7KG3.js +4172 -0
- package/dist/chunks/chunk-AQLVWVEN.js +39155 -0
- package/dist/chunks/chunk-BQ3DXZNT.js +968 -0
- package/dist/chunks/chunk-E65JE2CC.js +102 -0
- package/dist/chunks/chunk-EKPSCRJZ.js +26 -0
- package/dist/chunks/chunk-EOZFDJSY.js +18 -0
- package/dist/chunks/chunk-FDJURQMQ.js +4676 -0
- package/dist/chunks/chunk-FLKHKWZV.js +1854 -0
- package/dist/chunks/chunk-G6BUEBF5.js +192 -0
- package/dist/chunks/chunk-GBNIO3KP.js +771 -0
- package/dist/chunks/chunk-GGP5R3FU.js +129 -0
- package/dist/chunks/chunk-H5XJSH37.js +91 -0
- package/dist/chunks/chunk-IB5L4LKZ.js +1082 -0
- package/dist/chunks/chunk-IE7MNZ56.js +149 -0
- package/dist/chunks/chunk-IK7DLK2T.js +16112 -0
- package/dist/chunks/chunk-IUGPWINM.js +104 -0
- package/dist/chunks/chunk-J7HDA5GH.js +54 -0
- package/dist/chunks/chunk-JLYZNGYY.js +293 -0
- package/dist/chunks/chunk-JQ4NA5MX.js +250 -0
- package/dist/chunks/chunk-LL26LVRR.js +81 -0
- package/dist/chunks/chunk-LW5ZNGW7.js +127 -0
- package/dist/chunks/chunk-LWBSOTJP.js +1772 -0
- package/dist/chunks/chunk-MBGJBHYD.js +388 -0
- package/dist/chunks/chunk-NUKAG3YM.js +168 -0
- package/dist/chunks/chunk-O7I4ZOCC.js +58 -0
- package/dist/chunks/chunk-OWR3XNE3.js +48 -0
- package/dist/chunks/chunk-P3SKP5WM.js +27 -0
- package/dist/chunks/chunk-P4I4DMEU.js +342 -0
- package/dist/chunks/chunk-P5Q6F5IA.js +107 -0
- package/dist/chunks/chunk-PMSMUMUO.js +30 -0
- package/dist/chunks/chunk-QXRJ52T4.js +2977 -0
- package/dist/chunks/chunk-RQXPRFRM.js +90 -0
- package/dist/chunks/chunk-S7KYDPEM.js +1564 -0
- package/dist/chunks/chunk-SGGLJFUZ.js +68 -0
- package/dist/chunks/chunk-SOTR4CXR.js +34 -0
- package/dist/chunks/chunk-TEVP63TU.js +1717 -0
- package/dist/chunks/chunk-TNBMKNET.js +323 -0
- package/dist/chunks/chunk-TZ2YI2VH.js +87 -0
- package/dist/chunks/chunk-U6XOC6E4.js +903 -0
- package/dist/chunks/chunk-V5P25P7F.js +22 -0
- package/dist/chunks/chunk-WQ5CUZWR.js +333 -0
- package/dist/chunks/chunk-WU2BPWRP.js +12237 -0
- package/dist/chunks/chunk-XPKWKPWA.js +44 -0
- package/dist/chunks/chunk-XR53KVJD.js +33 -0
- package/dist/chunks/chunk-Y4JJYHUG.js +16 -0
- package/dist/chunks/chunk-YPQSDAEW.js +29 -0
- package/dist/chunks/chunk-ZB2UO4V2.js +135 -0
- package/dist/chunks/chunk-ZLCMHY2G.js +1528 -0
- package/dist/chunks/compile-vercel-config-XU3YY2CZ.js +32 -0
- package/dist/chunks/delete-EJ2V7KQO.js +144 -0
- package/dist/chunks/disable-BKRFMX4U.js +122 -0
- package/dist/chunks/discard-4WF34DXK.js +118 -0
- package/dist/chunks/edit-FQE7JGU3.js +509 -0
- package/dist/chunks/emit-flags-datafiles-QYKPNWPX.js +17 -0
- package/dist/chunks/enable-VCNMX63U.js +122 -0
- package/dist/chunks/export-3KNVJCQR.js +133 -0
- package/dist/chunks/list-43XQCGKH.js +382 -0
- package/dist/chunks/list-DUL6PHUR.js +394 -0
- package/dist/chunks/publish-CF7GVZK3.js +128 -0
- package/dist/chunks/query-KWKO7VWO.js +954 -0
- package/dist/chunks/reorder-GU65YMIN.js +259 -0
- package/dist/chunks/restore-Q7ENGWVJ.js +158 -0
- package/dist/chunks/routes-Q5CWG44T.js +20 -0
- package/dist/chunks/schema-PJKLO2K2.js +176 -0
- package/dist/chunks/stamp-RTPE2EBB.js +15 -0
- package/dist/chunks/types-563KUQRV.js +108 -0
- package/dist/chunks/update-route-version-E3V47KNI.js +13 -0
- package/dist/commands/build/index.js +1597 -0
- package/dist/commands/deploy/index.js +1711 -0
- package/dist/commands/dev/builder-worker.cjs +95 -0
- package/dist/commands/dev/index.js +20810 -0
- package/dist/commands/env/index.js +2154 -0
- package/dist/commands/link/index.js +225 -0
- package/dist/commands/list/index.js +528 -0
- package/dist/commands-bulk.js +29627 -0
- package/dist/get-latest-worker.cjs +272 -0
- package/dist/help.js +14 -0
- package/dist/index.js +24274 -0
- package/dist/vc.js +36 -0
- package/dist/version.mjs +1 -0
- package/package.json +254 -0
|
@@ -0,0 +1,1717 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath as __fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname as __dirname_ } from 'node:path';
|
|
4
|
+
const require = __createRequire(import.meta.url);
|
|
5
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = __dirname_(__filename);
|
|
7
|
+
import {
|
|
8
|
+
formatCondition,
|
|
9
|
+
formatTransform
|
|
10
|
+
} from "./chunk-TNBMKNET.js";
|
|
11
|
+
import {
|
|
12
|
+
output_manager_default
|
|
13
|
+
} from "./chunk-FDJURQMQ.js";
|
|
14
|
+
import {
|
|
15
|
+
require_source
|
|
16
|
+
} from "./chunk-S7KYDPEM.js";
|
|
17
|
+
import {
|
|
18
|
+
__toESM
|
|
19
|
+
} from "./chunk-TZ2YI2VH.js";
|
|
20
|
+
|
|
21
|
+
// src/util/routes/env.ts
|
|
22
|
+
function extractEnvVarNames(value) {
|
|
23
|
+
const names = /* @__PURE__ */ new Set();
|
|
24
|
+
for (const m of value.matchAll(/\$\{?([A-Z_][A-Z0-9_]*)\}?/g)) {
|
|
25
|
+
names.add(m[1]);
|
|
26
|
+
}
|
|
27
|
+
return Array.from(names);
|
|
28
|
+
}
|
|
29
|
+
function populateRouteEnv(route) {
|
|
30
|
+
const routeEnv = /* @__PURE__ */ new Set();
|
|
31
|
+
if (route.dest) {
|
|
32
|
+
for (const name of extractEnvVarNames(route.dest)) {
|
|
33
|
+
routeEnv.add(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (route.headers) {
|
|
37
|
+
for (const value of Object.values(route.headers)) {
|
|
38
|
+
for (const name of extractEnvVarNames(value)) {
|
|
39
|
+
routeEnv.add(name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
route.env = routeEnv.size > 0 ? Array.from(routeEnv) : void 0;
|
|
44
|
+
if (route.transforms) {
|
|
45
|
+
for (const transform of route.transforms) {
|
|
46
|
+
if (transform.args) {
|
|
47
|
+
const argsStr = Array.isArray(transform.args) ? transform.args.join(" ") : transform.args;
|
|
48
|
+
const names = extractEnvVarNames(argsStr);
|
|
49
|
+
transform.env = names.length > 0 ? names : void 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/util/routes/generate-route.ts
|
|
56
|
+
async function generateRoute(client, projectId, input, options = {}) {
|
|
57
|
+
const { teamId } = options;
|
|
58
|
+
const query = new URLSearchParams();
|
|
59
|
+
if (teamId)
|
|
60
|
+
query.set("teamId", teamId);
|
|
61
|
+
const queryString = query.toString();
|
|
62
|
+
const url = `/v1/projects/${projectId}/routes/generate${queryString ? `?${queryString}` : ""}`;
|
|
63
|
+
return await client.fetch(url, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json"
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(input)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/util/routes/parse-transforms.ts
|
|
73
|
+
function parseTransforms(values, type, op) {
|
|
74
|
+
return values.map((value) => parseTransform(value, type, op));
|
|
75
|
+
}
|
|
76
|
+
function parseTransform(input, type, op) {
|
|
77
|
+
if (op === "delete") {
|
|
78
|
+
const key2 = input.trim();
|
|
79
|
+
if (!key2) {
|
|
80
|
+
throw new Error("Delete operation requires a key");
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
type,
|
|
84
|
+
op,
|
|
85
|
+
target: { key: key2 }
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const eqIndex = input.indexOf("=");
|
|
89
|
+
if (eqIndex === -1) {
|
|
90
|
+
throw new Error(`Invalid format: "${input}". Expected format: key=value`);
|
|
91
|
+
}
|
|
92
|
+
const key = input.slice(0, eqIndex).trim();
|
|
93
|
+
const args = input.slice(eqIndex + 1);
|
|
94
|
+
if (!key) {
|
|
95
|
+
throw new Error("Transform key cannot be empty");
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
type,
|
|
99
|
+
op,
|
|
100
|
+
target: { key },
|
|
101
|
+
args
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function collectTransforms(flags) {
|
|
105
|
+
const transforms = [];
|
|
106
|
+
if (flags.setResponseHeader) {
|
|
107
|
+
transforms.push(
|
|
108
|
+
...parseTransforms(flags.setResponseHeader, "response.headers", "set")
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (flags.appendResponseHeader) {
|
|
112
|
+
transforms.push(
|
|
113
|
+
...parseTransforms(
|
|
114
|
+
flags.appendResponseHeader,
|
|
115
|
+
"response.headers",
|
|
116
|
+
"append"
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (flags.deleteResponseHeader) {
|
|
121
|
+
transforms.push(
|
|
122
|
+
...parseTransforms(
|
|
123
|
+
flags.deleteResponseHeader,
|
|
124
|
+
"response.headers",
|
|
125
|
+
"delete"
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (flags.setRequestHeader) {
|
|
130
|
+
transforms.push(
|
|
131
|
+
...parseTransforms(flags.setRequestHeader, "request.headers", "set")
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (flags.appendRequestHeader) {
|
|
135
|
+
transforms.push(
|
|
136
|
+
...parseTransforms(flags.appendRequestHeader, "request.headers", "append")
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (flags.deleteRequestHeader) {
|
|
140
|
+
transforms.push(
|
|
141
|
+
...parseTransforms(flags.deleteRequestHeader, "request.headers", "delete")
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (flags.setRequestQuery) {
|
|
145
|
+
transforms.push(
|
|
146
|
+
...parseTransforms(flags.setRequestQuery, "request.query", "set")
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (flags.appendRequestQuery) {
|
|
150
|
+
transforms.push(
|
|
151
|
+
...parseTransforms(flags.appendRequestQuery, "request.query", "append")
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (flags.deleteRequestQuery) {
|
|
155
|
+
transforms.push(
|
|
156
|
+
...parseTransforms(flags.deleteRequestQuery, "request.query", "delete")
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
return transforms;
|
|
160
|
+
}
|
|
161
|
+
function collectResponseHeaders(setHeaders) {
|
|
162
|
+
const headers = {};
|
|
163
|
+
for (const input of setHeaders) {
|
|
164
|
+
const eqIndex = input.indexOf("=");
|
|
165
|
+
if (eqIndex === -1) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Invalid header format: "${input}". Expected format: key=value`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const key = input.slice(0, eqIndex).trim();
|
|
171
|
+
const value = input.slice(eqIndex + 1);
|
|
172
|
+
if (!key) {
|
|
173
|
+
throw new Error("Header key cannot be empty");
|
|
174
|
+
}
|
|
175
|
+
headers[key] = value;
|
|
176
|
+
}
|
|
177
|
+
return headers;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/util/routes/interactive.ts
|
|
181
|
+
var MAX_NAME_LENGTH = 256;
|
|
182
|
+
var MAX_DESCRIPTION_LENGTH = 1024;
|
|
183
|
+
var MAX_CONDITIONS = 16;
|
|
184
|
+
var VALID_SYNTAXES = [
|
|
185
|
+
"regex",
|
|
186
|
+
"path-to-regexp",
|
|
187
|
+
"equals"
|
|
188
|
+
];
|
|
189
|
+
var REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308];
|
|
190
|
+
var VALID_ACTION_TYPES = [
|
|
191
|
+
"rewrite",
|
|
192
|
+
"redirect",
|
|
193
|
+
"set-status"
|
|
194
|
+
];
|
|
195
|
+
var ALL_ACTION_CHOICES = [
|
|
196
|
+
{ name: "Rewrite", value: "rewrite", exclusive: true },
|
|
197
|
+
{ name: "Redirect", value: "redirect", exclusive: true },
|
|
198
|
+
{ name: "Set Status Code", value: "set-status", exclusive: true },
|
|
199
|
+
{ name: "Response Headers", value: "response-headers" },
|
|
200
|
+
{ name: "Request Headers", value: "request-headers" },
|
|
201
|
+
{ name: "Request Query", value: "request-query" }
|
|
202
|
+
];
|
|
203
|
+
function stripQuotes(str) {
|
|
204
|
+
if (str.startsWith('"') && str.endsWith('"') && str.length >= 2) {
|
|
205
|
+
return str.slice(1, -1);
|
|
206
|
+
}
|
|
207
|
+
if (str.startsWith("'") && str.endsWith("'") && str.length >= 2) {
|
|
208
|
+
return str.slice(1, -1);
|
|
209
|
+
}
|
|
210
|
+
return str;
|
|
211
|
+
}
|
|
212
|
+
function extractTransformFlags(flags) {
|
|
213
|
+
return {
|
|
214
|
+
setResponseHeader: flags["--set-response-header"],
|
|
215
|
+
appendResponseHeader: flags["--append-response-header"],
|
|
216
|
+
deleteResponseHeader: flags["--delete-response-header"],
|
|
217
|
+
setRequestHeader: flags["--set-request-header"],
|
|
218
|
+
appendRequestHeader: flags["--append-request-header"],
|
|
219
|
+
deleteRequestHeader: flags["--delete-request-header"],
|
|
220
|
+
setRequestQuery: flags["--set-request-query"],
|
|
221
|
+
appendRequestQuery: flags["--append-request-query"],
|
|
222
|
+
deleteRequestQuery: flags["--delete-request-query"]
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function collectHeadersAndTransforms(transformFlags) {
|
|
226
|
+
const headers = transformFlags.setResponseHeader ? collectResponseHeaders(transformFlags.setResponseHeader) : {};
|
|
227
|
+
const transforms = collectTransforms({
|
|
228
|
+
...transformFlags,
|
|
229
|
+
setResponseHeader: void 0
|
|
230
|
+
// Already handled in headers
|
|
231
|
+
});
|
|
232
|
+
return { headers, transforms };
|
|
233
|
+
}
|
|
234
|
+
function hasAnyTransformFlags(flags) {
|
|
235
|
+
const tf = extractTransformFlags(flags);
|
|
236
|
+
return !!(tf.setResponseHeader || tf.appendResponseHeader || tf.deleteResponseHeader || tf.setRequestHeader || tf.appendRequestHeader || tf.deleteRequestHeader || tf.setRequestQuery || tf.appendRequestQuery || tf.deleteRequestQuery);
|
|
237
|
+
}
|
|
238
|
+
function validateActionFlags(action, dest, status) {
|
|
239
|
+
if (!action) {
|
|
240
|
+
if (dest || status !== void 0) {
|
|
241
|
+
return "--action is required when using --dest or --status. Use --action rewrite, --action redirect, or --action set-status.";
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
if (!VALID_ACTION_TYPES.includes(action)) {
|
|
246
|
+
return `Invalid action type: "${action}". Valid types: ${VALID_ACTION_TYPES.join(", ")}`;
|
|
247
|
+
}
|
|
248
|
+
switch (action) {
|
|
249
|
+
case "rewrite":
|
|
250
|
+
if (!dest)
|
|
251
|
+
return "--action rewrite requires --dest.";
|
|
252
|
+
if (status !== void 0)
|
|
253
|
+
return "--action rewrite does not accept --status.";
|
|
254
|
+
break;
|
|
255
|
+
case "redirect":
|
|
256
|
+
if (!dest)
|
|
257
|
+
return "--action redirect requires --dest.";
|
|
258
|
+
if (status === void 0)
|
|
259
|
+
return `--action redirect requires --status (${REDIRECT_STATUS_CODES.join(", ")}).`;
|
|
260
|
+
if (!REDIRECT_STATUS_CODES.includes(status))
|
|
261
|
+
return `Invalid redirect status: ${status}. Must be one of: ${REDIRECT_STATUS_CODES.join(", ")}`;
|
|
262
|
+
break;
|
|
263
|
+
case "set-status":
|
|
264
|
+
if (dest)
|
|
265
|
+
return "--action set-status does not accept --dest.";
|
|
266
|
+
if (status === void 0)
|
|
267
|
+
return "--action set-status requires --status.";
|
|
268
|
+
if (status < 100 || status > 599)
|
|
269
|
+
return "Status code must be between 100 and 599.";
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
async function collectActionDetails(client, actionType, flags) {
|
|
275
|
+
switch (actionType) {
|
|
276
|
+
case "rewrite": {
|
|
277
|
+
const dest = await client.input.text({
|
|
278
|
+
message: "Destination URL:",
|
|
279
|
+
validate: (val) => val ? true : "Destination is required"
|
|
280
|
+
});
|
|
281
|
+
Object.assign(flags, { "--dest": dest });
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case "redirect": {
|
|
285
|
+
const dest = await client.input.text({
|
|
286
|
+
message: "Destination URL:",
|
|
287
|
+
validate: (val) => val ? true : "Destination is required"
|
|
288
|
+
});
|
|
289
|
+
const status = await client.input.select({
|
|
290
|
+
message: "Status code:",
|
|
291
|
+
choices: [
|
|
292
|
+
{ name: "307 - Temporary Redirect", value: 307 },
|
|
293
|
+
{ name: "308 - Permanent Redirect", value: 308 },
|
|
294
|
+
{ name: "301 - Moved Permanently", value: 301 },
|
|
295
|
+
{ name: "302 - Found", value: 302 },
|
|
296
|
+
{ name: "303 - See Other", value: 303 }
|
|
297
|
+
]
|
|
298
|
+
});
|
|
299
|
+
Object.assign(flags, { "--dest": dest, "--status": status });
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case "set-status": {
|
|
303
|
+
const statusCode = await client.input.text({
|
|
304
|
+
message: "HTTP status code:",
|
|
305
|
+
validate: (val) => {
|
|
306
|
+
const num = parseInt(val, 10);
|
|
307
|
+
if (isNaN(num) || num < 100 || num > 599) {
|
|
308
|
+
return "Status code must be between 100 and 599";
|
|
309
|
+
}
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
Object.assign(flags, { "--status": parseInt(statusCode, 10) });
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case "response-headers": {
|
|
317
|
+
await collectInteractiveHeaders(client, "response", flags);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
case "request-headers": {
|
|
321
|
+
await collectInteractiveHeaders(client, "request-header", flags);
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "request-query": {
|
|
325
|
+
await collectInteractiveHeaders(client, "request-query", flags);
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function collectInteractiveConditions(client, flags) {
|
|
331
|
+
let addMore = true;
|
|
332
|
+
while (addMore) {
|
|
333
|
+
const currentHas = flags["--has"] || [];
|
|
334
|
+
const currentMissing = flags["--missing"] || [];
|
|
335
|
+
if (currentHas.length > 0 || currentMissing.length > 0) {
|
|
336
|
+
output_manager_default.log("\nCurrent conditions:");
|
|
337
|
+
for (const c of currentHas) {
|
|
338
|
+
output_manager_default.print(` has: ${c}
|
|
339
|
+
`);
|
|
340
|
+
}
|
|
341
|
+
for (const c of currentMissing) {
|
|
342
|
+
output_manager_default.print(` does not have: ${c}
|
|
343
|
+
`);
|
|
344
|
+
}
|
|
345
|
+
output_manager_default.print("\n");
|
|
346
|
+
}
|
|
347
|
+
const conditionType = await client.input.select({
|
|
348
|
+
message: "Condition type:",
|
|
349
|
+
choices: [
|
|
350
|
+
{ name: "has - Request must have this", value: "has" },
|
|
351
|
+
{
|
|
352
|
+
name: "does not have - Request must NOT have this",
|
|
353
|
+
value: "missing"
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
});
|
|
357
|
+
const targetType = await client.input.select({
|
|
358
|
+
message: "What to check:",
|
|
359
|
+
choices: [
|
|
360
|
+
{ name: "Header", value: "header" },
|
|
361
|
+
{ name: "Cookie", value: "cookie" },
|
|
362
|
+
{ name: "Query Parameter", value: "query" },
|
|
363
|
+
{ name: "Host", value: "host" }
|
|
364
|
+
]
|
|
365
|
+
});
|
|
366
|
+
let conditionValue;
|
|
367
|
+
if (targetType === "host") {
|
|
368
|
+
const operator = await client.input.select({
|
|
369
|
+
message: "How to match the host:",
|
|
370
|
+
choices: [
|
|
371
|
+
{ name: "Equals", value: "eq" },
|
|
372
|
+
{ name: "Contains", value: "contains" },
|
|
373
|
+
{ name: "Matches (regex)", value: "re" }
|
|
374
|
+
]
|
|
375
|
+
});
|
|
376
|
+
const hostInput = await client.input.text({
|
|
377
|
+
message: operator === "re" ? "Host pattern (regex):" : "Host value:",
|
|
378
|
+
validate: (val) => {
|
|
379
|
+
if (!val)
|
|
380
|
+
return "Host value is required";
|
|
381
|
+
if (operator === "re") {
|
|
382
|
+
try {
|
|
383
|
+
new RegExp(val);
|
|
384
|
+
return true;
|
|
385
|
+
} catch {
|
|
386
|
+
return "Invalid regex pattern";
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
conditionValue = `host:${operator}=${hostInput}`;
|
|
393
|
+
} else {
|
|
394
|
+
const key = await client.input.text({
|
|
395
|
+
message: `${targetType.charAt(0).toUpperCase() + targetType.slice(1)} name:`,
|
|
396
|
+
validate: (val) => val ? true : `${targetType} name is required`
|
|
397
|
+
});
|
|
398
|
+
const operator = await client.input.select({
|
|
399
|
+
message: "How to match the value:",
|
|
400
|
+
choices: [
|
|
401
|
+
{ name: "Exists (any value)", value: "exists" },
|
|
402
|
+
{ name: "Equals", value: "eq" },
|
|
403
|
+
{ name: "Contains", value: "contains" },
|
|
404
|
+
{ name: "Matches (regex)", value: "re" }
|
|
405
|
+
]
|
|
406
|
+
});
|
|
407
|
+
if (operator === "exists") {
|
|
408
|
+
conditionValue = `${targetType}:${key}:exists`;
|
|
409
|
+
} else {
|
|
410
|
+
const valueInput = await client.input.text({
|
|
411
|
+
message: operator === "re" ? "Value pattern (regex):" : "Value:",
|
|
412
|
+
validate: (val) => {
|
|
413
|
+
if (!val)
|
|
414
|
+
return "Value is required";
|
|
415
|
+
if (operator === "re") {
|
|
416
|
+
try {
|
|
417
|
+
new RegExp(val);
|
|
418
|
+
return true;
|
|
419
|
+
} catch {
|
|
420
|
+
return "Invalid regex pattern";
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
conditionValue = `${targetType}:${key}:${operator}=${valueInput}`;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const flagName = conditionType === "has" ? "--has" : "--missing";
|
|
430
|
+
const existing = flags[flagName] || [];
|
|
431
|
+
flags[flagName] = [...existing, conditionValue];
|
|
432
|
+
const totalConditions = (flags["--has"] || []).length + (flags["--missing"] || []).length;
|
|
433
|
+
if (totalConditions >= MAX_CONDITIONS) {
|
|
434
|
+
output_manager_default.warn(`Maximum ${MAX_CONDITIONS} conditions reached.`);
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
addMore = await client.input.confirm("Add another condition?", false);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function formatCollectedItems(flags, type) {
|
|
441
|
+
const items = [];
|
|
442
|
+
const prefix = type === "response" ? "response-header" : type === "request-header" ? "request-header" : "request-query";
|
|
443
|
+
const setItems = flags[`--set-${prefix}`] || [];
|
|
444
|
+
const appendItems = flags[`--append-${prefix}`] || [];
|
|
445
|
+
const deleteItems = flags[`--delete-${prefix}`] || [];
|
|
446
|
+
for (const item of setItems) {
|
|
447
|
+
items.push(` set: ${item}`);
|
|
448
|
+
}
|
|
449
|
+
for (const item of appendItems) {
|
|
450
|
+
items.push(` append: ${item}`);
|
|
451
|
+
}
|
|
452
|
+
for (const item of deleteItems) {
|
|
453
|
+
items.push(` delete: ${item}`);
|
|
454
|
+
}
|
|
455
|
+
return items;
|
|
456
|
+
}
|
|
457
|
+
async function collectInteractiveHeaders(client, type, flags) {
|
|
458
|
+
const flagName = type === "response" ? "--set-response-header" : type === "request-header" ? "--set-request-header" : "--set-request-query";
|
|
459
|
+
const sectionName = type === "response" ? "Response Headers" : type === "request-header" ? "Request Headers" : "Request Query Parameters";
|
|
460
|
+
const itemName = type === "response" ? "response header" : type === "request-header" ? "request header" : "query parameter";
|
|
461
|
+
output_manager_default.log(`
|
|
462
|
+
--- ${sectionName} ---`);
|
|
463
|
+
let addMore = true;
|
|
464
|
+
while (addMore) {
|
|
465
|
+
const collected = formatCollectedItems(flags, type);
|
|
466
|
+
if (collected.length > 0) {
|
|
467
|
+
output_manager_default.log(`
|
|
468
|
+
Current ${sectionName.toLowerCase()}:`);
|
|
469
|
+
for (const item of collected) {
|
|
470
|
+
output_manager_default.print(`${item}
|
|
471
|
+
`);
|
|
472
|
+
}
|
|
473
|
+
output_manager_default.print("\n");
|
|
474
|
+
}
|
|
475
|
+
const op = await client.input.select({
|
|
476
|
+
message: `${sectionName} operation:`,
|
|
477
|
+
choices: [
|
|
478
|
+
{ name: "Set", value: "set" },
|
|
479
|
+
{ name: "Append", value: "append" },
|
|
480
|
+
{ name: "Delete", value: "delete" }
|
|
481
|
+
]
|
|
482
|
+
});
|
|
483
|
+
const key = await client.input.text({
|
|
484
|
+
message: `${itemName.charAt(0).toUpperCase() + itemName.slice(1)} name:`,
|
|
485
|
+
validate: (val) => val ? true : `${itemName} name is required`
|
|
486
|
+
});
|
|
487
|
+
if (op === "delete") {
|
|
488
|
+
const opFlagName = flagName.replace("--set-", "--delete-");
|
|
489
|
+
const existing = flags[opFlagName] || [];
|
|
490
|
+
flags[opFlagName] = [...existing, key];
|
|
491
|
+
} else {
|
|
492
|
+
const value = await client.input.text({
|
|
493
|
+
message: `${itemName.charAt(0).toUpperCase() + itemName.slice(1)} value:`
|
|
494
|
+
});
|
|
495
|
+
const opFlagName = op === "append" ? flagName.replace("--set-", "--append-") : flagName;
|
|
496
|
+
const existing = flags[opFlagName] || [];
|
|
497
|
+
flags[opFlagName] = [...existing, `${key}=${value}`];
|
|
498
|
+
}
|
|
499
|
+
addMore = await client.input.confirm(`Add another ${itemName}?`, false);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// src/util/routes/ai-transform.ts
|
|
504
|
+
var import_chalk = __toESM(require_source(), 1);
|
|
505
|
+
|
|
506
|
+
// src/util/routes/parse-conditions.ts
|
|
507
|
+
var CONDITION_OPERATORS = [
|
|
508
|
+
"eq",
|
|
509
|
+
"contains",
|
|
510
|
+
"re",
|
|
511
|
+
"exists"
|
|
512
|
+
];
|
|
513
|
+
function escapeRegExp(str) {
|
|
514
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
515
|
+
}
|
|
516
|
+
function buildConditionValue(operator, value) {
|
|
517
|
+
if (operator === "exists")
|
|
518
|
+
return void 0;
|
|
519
|
+
if (operator === "re")
|
|
520
|
+
return value;
|
|
521
|
+
const escapedValue = escapeRegExp(value);
|
|
522
|
+
if (operator === "contains")
|
|
523
|
+
return `.*${escapedValue}.*`;
|
|
524
|
+
return `^${escapedValue}$`;
|
|
525
|
+
}
|
|
526
|
+
function validateRegexPattern(pattern, context) {
|
|
527
|
+
try {
|
|
528
|
+
new RegExp(pattern);
|
|
529
|
+
} catch (e) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
`Invalid regex in ${context}: "${pattern}". ${e instanceof Error ? e.message : ""}`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function parseOperatorValue(valuePart) {
|
|
536
|
+
if (valuePart === "exists") {
|
|
537
|
+
return { operator: "exists", rawValue: "" };
|
|
538
|
+
}
|
|
539
|
+
const eqIdx = valuePart.indexOf("=");
|
|
540
|
+
if (eqIdx === -1)
|
|
541
|
+
return null;
|
|
542
|
+
const maybeOp = valuePart.slice(0, eqIdx);
|
|
543
|
+
if (CONDITION_OPERATORS.includes(maybeOp)) {
|
|
544
|
+
return {
|
|
545
|
+
operator: maybeOp,
|
|
546
|
+
rawValue: valuePart.slice(eqIdx + 1)
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
function parseConditions(conditions) {
|
|
552
|
+
return conditions.map(parseCondition);
|
|
553
|
+
}
|
|
554
|
+
function parseCondition(condition) {
|
|
555
|
+
const parts = condition.split(":");
|
|
556
|
+
if (parts.length < 2) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`Invalid condition format: "${condition}". Expected format: type:key or type:key:value`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
const type = parts[0].toLowerCase();
|
|
562
|
+
const validTypes = ["header", "cookie", "query", "host"];
|
|
563
|
+
if (!validTypes.includes(type)) {
|
|
564
|
+
throw new Error(
|
|
565
|
+
`Invalid condition type: "${type}". Valid types: ${validTypes.join(", ")}`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
if (type === "host") {
|
|
569
|
+
const rawValue = parts.slice(1).join(":");
|
|
570
|
+
if (!rawValue) {
|
|
571
|
+
throw new Error("Host condition requires a value");
|
|
572
|
+
}
|
|
573
|
+
const opResult2 = parseOperatorValue(rawValue);
|
|
574
|
+
if (opResult2) {
|
|
575
|
+
if (opResult2.operator === "exists") {
|
|
576
|
+
throw new Error(
|
|
577
|
+
'Host condition does not support "exists" operator (host always has a value)'
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
if (opResult2.operator !== "re" && !opResult2.rawValue) {
|
|
581
|
+
throw new Error(
|
|
582
|
+
`Host condition with "${opResult2.operator}" operator requires a value`
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
const compiledValue = buildConditionValue(
|
|
586
|
+
opResult2.operator,
|
|
587
|
+
opResult2.rawValue
|
|
588
|
+
);
|
|
589
|
+
if (compiledValue !== void 0) {
|
|
590
|
+
validateRegexPattern(compiledValue, "host condition");
|
|
591
|
+
}
|
|
592
|
+
return { type: "host", value: compiledValue };
|
|
593
|
+
}
|
|
594
|
+
validateRegexPattern(rawValue, "host condition");
|
|
595
|
+
return { type: "host", value: rawValue };
|
|
596
|
+
}
|
|
597
|
+
const key = parts[1];
|
|
598
|
+
if (!key) {
|
|
599
|
+
throw new Error(`${type} condition requires a key`);
|
|
600
|
+
}
|
|
601
|
+
const valuePart = parts.length > 2 ? parts.slice(2).join(":") : void 0;
|
|
602
|
+
if (valuePart === void 0) {
|
|
603
|
+
return { type, key };
|
|
604
|
+
}
|
|
605
|
+
const opResult = parseOperatorValue(valuePart);
|
|
606
|
+
if (opResult) {
|
|
607
|
+
if (opResult.operator === "exists") {
|
|
608
|
+
return { type, key };
|
|
609
|
+
}
|
|
610
|
+
if (opResult.operator !== "re" && !opResult.rawValue) {
|
|
611
|
+
throw new Error(
|
|
612
|
+
`Condition "${opResult.operator}" operator requires a value after "="`
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
const compiledValue = buildConditionValue(
|
|
616
|
+
opResult.operator,
|
|
617
|
+
opResult.rawValue
|
|
618
|
+
);
|
|
619
|
+
if (compiledValue !== void 0) {
|
|
620
|
+
validateRegexPattern(compiledValue, `${type} condition value`);
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
type,
|
|
624
|
+
key,
|
|
625
|
+
...compiledValue !== void 0 && { value: compiledValue }
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
validateRegexPattern(valuePart, `${type} condition value`);
|
|
629
|
+
return {
|
|
630
|
+
type,
|
|
631
|
+
key,
|
|
632
|
+
value: valuePart
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function formatCondition2(field) {
|
|
636
|
+
if (field.type === "host") {
|
|
637
|
+
return `host:${field.value}`;
|
|
638
|
+
}
|
|
639
|
+
if (field.value) {
|
|
640
|
+
return `${field.type}:${field.key}:${field.value}`;
|
|
641
|
+
}
|
|
642
|
+
return `${field.type}:${field.key}`;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/util/routes/ai-transform.ts
|
|
646
|
+
function generatedRouteToAddInput(generated) {
|
|
647
|
+
const hasConditions = [];
|
|
648
|
+
const missingConditions = [];
|
|
649
|
+
const headers = {};
|
|
650
|
+
const transforms = [];
|
|
651
|
+
let dest;
|
|
652
|
+
let status;
|
|
653
|
+
if (generated.conditions) {
|
|
654
|
+
for (const c of generated.conditions) {
|
|
655
|
+
const compiledValue = c.value !== void 0 ? buildConditionValue(c.operator, c.value) : void 0;
|
|
656
|
+
const field = c.field === "host" ? { type: "host", value: compiledValue ?? c.value ?? "" } : {
|
|
657
|
+
type: c.field,
|
|
658
|
+
key: c.key ?? "",
|
|
659
|
+
...compiledValue !== void 0 && { value: compiledValue }
|
|
660
|
+
};
|
|
661
|
+
if (c.missing) {
|
|
662
|
+
missingConditions.push(field);
|
|
663
|
+
} else {
|
|
664
|
+
hasConditions.push(field);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
for (const action of generated.actions) {
|
|
669
|
+
switch (action.type) {
|
|
670
|
+
case "rewrite":
|
|
671
|
+
dest = action.dest;
|
|
672
|
+
break;
|
|
673
|
+
case "redirect":
|
|
674
|
+
dest = action.dest;
|
|
675
|
+
status = action.status;
|
|
676
|
+
break;
|
|
677
|
+
case "set-status":
|
|
678
|
+
status = action.status;
|
|
679
|
+
break;
|
|
680
|
+
case "modify": {
|
|
681
|
+
if (!action.headers)
|
|
682
|
+
break;
|
|
683
|
+
if (action.subType === "response-headers") {
|
|
684
|
+
for (const h of action.headers) {
|
|
685
|
+
if (h.op === "set") {
|
|
686
|
+
headers[h.key] = h.value ?? "";
|
|
687
|
+
} else {
|
|
688
|
+
transforms.push({
|
|
689
|
+
type: "response.headers",
|
|
690
|
+
op: h.op,
|
|
691
|
+
target: { key: h.key },
|
|
692
|
+
...h.op !== "delete" && h.value && { args: h.value }
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
} else if (action.subType === "transform-request-header") {
|
|
697
|
+
for (const h of action.headers) {
|
|
698
|
+
transforms.push({
|
|
699
|
+
type: "request.headers",
|
|
700
|
+
op: h.op,
|
|
701
|
+
target: { key: h.key },
|
|
702
|
+
...h.op !== "delete" && h.value && { args: h.value }
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
} else if (action.subType === "transform-request-query") {
|
|
706
|
+
for (const h of action.headers) {
|
|
707
|
+
transforms.push({
|
|
708
|
+
type: "request.query",
|
|
709
|
+
op: h.op,
|
|
710
|
+
target: { key: h.key },
|
|
711
|
+
...h.op !== "delete" && h.value && { args: h.value }
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
name: generated.name,
|
|
721
|
+
description: generated.description || void 0,
|
|
722
|
+
srcSyntax: generated.pathCondition.syntax,
|
|
723
|
+
route: {
|
|
724
|
+
src: generated.pathCondition.value,
|
|
725
|
+
...dest !== void 0 && { dest },
|
|
726
|
+
...status !== void 0 && { status },
|
|
727
|
+
...Object.keys(headers).length > 0 && { headers },
|
|
728
|
+
...transforms.length > 0 && { transforms },
|
|
729
|
+
...hasConditions.length > 0 && { has: hasConditions },
|
|
730
|
+
...missingConditions.length > 0 && { missing: missingConditions }
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function convertRouteToCurrentRoute(generated) {
|
|
735
|
+
return {
|
|
736
|
+
name: generated.name,
|
|
737
|
+
description: generated.description || void 0,
|
|
738
|
+
pathCondition: generated.pathCondition,
|
|
739
|
+
conditions: generated.conditions,
|
|
740
|
+
actions: generated.actions
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function routingRuleToCurrentRoute(rule) {
|
|
744
|
+
const conditions = [];
|
|
745
|
+
const actions = [];
|
|
746
|
+
if (rule.route.has) {
|
|
747
|
+
for (const c of rule.route.has) {
|
|
748
|
+
conditions.push({
|
|
749
|
+
field: c.type,
|
|
750
|
+
operator: c.value !== void 0 ? "re" : "exists",
|
|
751
|
+
key: c.key,
|
|
752
|
+
value: c.value !== void 0 ? typeof c.value === "string" ? c.value : JSON.stringify(c.value) : void 0,
|
|
753
|
+
missing: false
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
if (rule.route.missing) {
|
|
758
|
+
for (const c of rule.route.missing) {
|
|
759
|
+
conditions.push({
|
|
760
|
+
field: c.type,
|
|
761
|
+
operator: c.value !== void 0 ? "re" : "exists",
|
|
762
|
+
key: c.key,
|
|
763
|
+
value: c.value !== void 0 ? typeof c.value === "string" ? c.value : JSON.stringify(c.value) : void 0,
|
|
764
|
+
missing: true
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
const isRedirect = rule.route.dest && rule.route.status && REDIRECT_STATUS_CODES.includes(rule.route.status);
|
|
769
|
+
if (isRedirect) {
|
|
770
|
+
actions.push({
|
|
771
|
+
type: "redirect",
|
|
772
|
+
dest: rule.route.dest,
|
|
773
|
+
status: rule.route.status
|
|
774
|
+
});
|
|
775
|
+
} else if (rule.route.dest) {
|
|
776
|
+
actions.push({ type: "rewrite", dest: rule.route.dest });
|
|
777
|
+
} else if (rule.route.status) {
|
|
778
|
+
actions.push({ type: "set-status", status: rule.route.status });
|
|
779
|
+
}
|
|
780
|
+
const responseHeaders = rule.route.headers ? Object.entries(rule.route.headers).map(([key, value]) => ({
|
|
781
|
+
key,
|
|
782
|
+
value,
|
|
783
|
+
op: "set"
|
|
784
|
+
})) : [];
|
|
785
|
+
const allTransforms = rule.route.transforms ?? [];
|
|
786
|
+
const responseHeaderTransforms = allTransforms.filter((t) => t.type === "response.headers").map((t) => ({
|
|
787
|
+
key: typeof t.target.key === "string" ? t.target.key : String(t.target.key),
|
|
788
|
+
value: t.args,
|
|
789
|
+
op: t.op
|
|
790
|
+
}));
|
|
791
|
+
const allResponseHeaders = [...responseHeaders, ...responseHeaderTransforms];
|
|
792
|
+
if (allResponseHeaders.length > 0) {
|
|
793
|
+
actions.push({
|
|
794
|
+
type: "modify",
|
|
795
|
+
subType: "response-headers",
|
|
796
|
+
headers: allResponseHeaders
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
const requestHeaders = allTransforms.filter((t) => t.type === "request.headers").map((t) => ({
|
|
800
|
+
key: typeof t.target.key === "string" ? t.target.key : String(t.target.key),
|
|
801
|
+
value: t.args,
|
|
802
|
+
op: t.op
|
|
803
|
+
}));
|
|
804
|
+
if (requestHeaders.length > 0) {
|
|
805
|
+
actions.push({
|
|
806
|
+
type: "modify",
|
|
807
|
+
subType: "transform-request-header",
|
|
808
|
+
headers: requestHeaders
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
const requestQuery = allTransforms.filter((t) => t.type === "request.query").map((t) => ({
|
|
812
|
+
key: typeof t.target.key === "string" ? t.target.key : String(t.target.key),
|
|
813
|
+
value: t.args,
|
|
814
|
+
op: t.op
|
|
815
|
+
}));
|
|
816
|
+
if (requestQuery.length > 0) {
|
|
817
|
+
actions.push({
|
|
818
|
+
type: "modify",
|
|
819
|
+
subType: "transform-request-query",
|
|
820
|
+
headers: requestQuery
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
name: rule.name,
|
|
825
|
+
description: rule.description,
|
|
826
|
+
pathCondition: {
|
|
827
|
+
value: rule.route.src,
|
|
828
|
+
syntax: rule.srcSyntax ?? "regex"
|
|
829
|
+
},
|
|
830
|
+
...conditions.length > 0 && { conditions },
|
|
831
|
+
actions
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function printGeneratedRoutePreview(generated) {
|
|
835
|
+
output_manager_default.print("\n");
|
|
836
|
+
output_manager_default.print(` ${import_chalk.default.bold("Generated Route:")}
|
|
837
|
+
`);
|
|
838
|
+
output_manager_default.print(` ${import_chalk.default.cyan("Name:")} ${generated.name}
|
|
839
|
+
`);
|
|
840
|
+
if (generated.description) {
|
|
841
|
+
output_manager_default.print(` ${import_chalk.default.cyan("Description:")} ${generated.description}
|
|
842
|
+
`);
|
|
843
|
+
}
|
|
844
|
+
output_manager_default.print(
|
|
845
|
+
` ${import_chalk.default.cyan("Source:")} ${generated.pathCondition.value}
|
|
846
|
+
`
|
|
847
|
+
);
|
|
848
|
+
if (generated.conditions && generated.conditions.length > 0) {
|
|
849
|
+
output_manager_default.print(` ${import_chalk.default.cyan("Conditions:")}
|
|
850
|
+
`);
|
|
851
|
+
for (const c of generated.conditions) {
|
|
852
|
+
const prefix = c.missing ? "does not have" : "has";
|
|
853
|
+
const operatorLabel = c.operator === "eq" ? "equal to" : c.operator === "contains" ? "containing" : c.operator === "re" ? "matching" : "";
|
|
854
|
+
const key = c.key ? ` ${import_chalk.default.cyan(`"${c.key}"`)}` : "";
|
|
855
|
+
const value = c.operator === "exists" || !c.value ? "" : ` ${operatorLabel} ${import_chalk.default.cyan(`"${c.value}"`)}`;
|
|
856
|
+
output_manager_default.print(` ${import_chalk.default.gray(prefix)} ${c.field}${key}${value}
|
|
857
|
+
`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
for (const action of generated.actions) {
|
|
861
|
+
if (action.type === "rewrite" && action.dest) {
|
|
862
|
+
output_manager_default.print(
|
|
863
|
+
` ${import_chalk.default.cyan("Action:")} Rewrite \u2192 ${action.dest}
|
|
864
|
+
`
|
|
865
|
+
);
|
|
866
|
+
} else if (action.type === "redirect" && action.dest) {
|
|
867
|
+
output_manager_default.print(
|
|
868
|
+
` ${import_chalk.default.cyan("Action:")} Redirect \u2192 ${action.dest} (${action.status})
|
|
869
|
+
`
|
|
870
|
+
);
|
|
871
|
+
} else if (action.type === "set-status" && action.status) {
|
|
872
|
+
output_manager_default.print(
|
|
873
|
+
` ${import_chalk.default.cyan("Action:")} Set Status ${action.status}
|
|
874
|
+
`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
for (const action of generated.actions) {
|
|
879
|
+
if (action.type === "modify" && action.headers) {
|
|
880
|
+
const label = action.subType === "response-headers" ? "Response Headers" : action.subType === "transform-request-header" ? "Request Headers" : "Request Query";
|
|
881
|
+
output_manager_default.print(` ${import_chalk.default.cyan(`${label}:`)}
|
|
882
|
+
`);
|
|
883
|
+
for (const h of action.headers) {
|
|
884
|
+
if (h.op === "delete") {
|
|
885
|
+
output_manager_default.print(` ${import_chalk.default.yellow(h.op)} ${import_chalk.default.cyan(h.key)}
|
|
886
|
+
`);
|
|
887
|
+
} else {
|
|
888
|
+
output_manager_default.print(
|
|
889
|
+
` ${import_chalk.default.yellow(h.op)} ${import_chalk.default.cyan(h.key)} = ${h.value}
|
|
890
|
+
`
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
output_manager_default.print("\n");
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// src/commands/routes/edit-interactive.ts
|
|
900
|
+
var import_chalk2 = __toESM(require_source(), 1);
|
|
901
|
+
function getPrimaryActionType(route) {
|
|
902
|
+
const { dest, status } = route.route;
|
|
903
|
+
if (dest && status && REDIRECT_STATUS_CODES.includes(status)) {
|
|
904
|
+
return "redirect";
|
|
905
|
+
}
|
|
906
|
+
if (dest)
|
|
907
|
+
return "rewrite";
|
|
908
|
+
if (status)
|
|
909
|
+
return "set-status";
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
function getPrimaryActionLabel(route) {
|
|
913
|
+
const actionType = getPrimaryActionType(route);
|
|
914
|
+
switch (actionType) {
|
|
915
|
+
case "rewrite":
|
|
916
|
+
return `Rewrite \u2192 ${route.route.dest}`;
|
|
917
|
+
case "redirect":
|
|
918
|
+
return `Redirect \u2192 ${route.route.dest} (${route.route.status})`;
|
|
919
|
+
case "set-status":
|
|
920
|
+
return `Set Status ${route.route.status}`;
|
|
921
|
+
default:
|
|
922
|
+
return "(none)";
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function getResponseHeaders(route) {
|
|
926
|
+
const headers = route.route.headers ?? {};
|
|
927
|
+
return Object.entries(headers).map(([key, value]) => ({ key, value }));
|
|
928
|
+
}
|
|
929
|
+
function getTransformsByType(route, type) {
|
|
930
|
+
const transforms = route.route.transforms ?? [];
|
|
931
|
+
return transforms.filter((t) => t.type === type);
|
|
932
|
+
}
|
|
933
|
+
function printRouteConfig(route) {
|
|
934
|
+
output_manager_default.print("\n");
|
|
935
|
+
output_manager_default.print(` ${import_chalk2.default.cyan("Name:")} ${route.name}
|
|
936
|
+
`);
|
|
937
|
+
if (route.description) {
|
|
938
|
+
output_manager_default.print(` ${import_chalk2.default.cyan("Description:")} ${route.description}
|
|
939
|
+
`);
|
|
940
|
+
}
|
|
941
|
+
output_manager_default.print(
|
|
942
|
+
` ${import_chalk2.default.cyan("Source:")} ${route.route.src} ${import_chalk2.default.gray(`(${route.srcSyntax ?? "regex"})`)}
|
|
943
|
+
`
|
|
944
|
+
);
|
|
945
|
+
output_manager_default.print(
|
|
946
|
+
` ${import_chalk2.default.cyan("Status:")} ${route.enabled === false ? import_chalk2.default.red("Disabled") : import_chalk2.default.green("Enabled")}
|
|
947
|
+
`
|
|
948
|
+
);
|
|
949
|
+
const actionLabel = getPrimaryActionLabel(route);
|
|
950
|
+
output_manager_default.print(` ${import_chalk2.default.cyan("Action:")} ${actionLabel}
|
|
951
|
+
`);
|
|
952
|
+
const hasConds = route.route.has ?? [];
|
|
953
|
+
if (hasConds.length > 0) {
|
|
954
|
+
output_manager_default.print(`
|
|
955
|
+
${import_chalk2.default.cyan("Has conditions:")}
|
|
956
|
+
`);
|
|
957
|
+
for (const c of hasConds) {
|
|
958
|
+
output_manager_default.print(` ${formatCondition(c)}
|
|
959
|
+
`);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const missingConds = route.route.missing ?? [];
|
|
963
|
+
if (missingConds.length > 0) {
|
|
964
|
+
output_manager_default.print(`
|
|
965
|
+
${import_chalk2.default.cyan("Does not have conditions:")}
|
|
966
|
+
`);
|
|
967
|
+
for (const c of missingConds) {
|
|
968
|
+
output_manager_default.print(` ${formatCondition(c)}
|
|
969
|
+
`);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const responseHeaders = getResponseHeaders(route);
|
|
973
|
+
if (responseHeaders.length > 0) {
|
|
974
|
+
output_manager_default.print(`
|
|
975
|
+
${import_chalk2.default.cyan("Response Headers:")}
|
|
976
|
+
`);
|
|
977
|
+
for (const h of responseHeaders) {
|
|
978
|
+
output_manager_default.print(` ${import_chalk2.default.cyan(h.key)} = ${h.value}
|
|
979
|
+
`);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
const requestHeaders = getTransformsByType(route, "request.headers");
|
|
983
|
+
if (requestHeaders.length > 0) {
|
|
984
|
+
output_manager_default.print(`
|
|
985
|
+
${import_chalk2.default.cyan("Request Headers:")}
|
|
986
|
+
`);
|
|
987
|
+
for (const t of requestHeaders) {
|
|
988
|
+
output_manager_default.print(` ${formatTransform(t)}
|
|
989
|
+
`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
const requestQuery = getTransformsByType(route, "request.query");
|
|
993
|
+
if (requestQuery.length > 0) {
|
|
994
|
+
output_manager_default.print(`
|
|
995
|
+
${import_chalk2.default.cyan("Request Query:")}
|
|
996
|
+
`);
|
|
997
|
+
for (const t of requestQuery) {
|
|
998
|
+
output_manager_default.print(` ${formatTransform(t)}
|
|
999
|
+
`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
output_manager_default.print("\n");
|
|
1003
|
+
}
|
|
1004
|
+
function cloneRoute(route) {
|
|
1005
|
+
return JSON.parse(JSON.stringify(route));
|
|
1006
|
+
}
|
|
1007
|
+
function applyFlagMutations(route, flags) {
|
|
1008
|
+
if (flags["--name"] !== void 0) {
|
|
1009
|
+
const name = flags["--name"];
|
|
1010
|
+
if (name.length > MAX_NAME_LENGTH) {
|
|
1011
|
+
return `Name must be ${MAX_NAME_LENGTH} characters or less.`;
|
|
1012
|
+
}
|
|
1013
|
+
route.name = name;
|
|
1014
|
+
}
|
|
1015
|
+
if (flags["--description"] !== void 0) {
|
|
1016
|
+
const desc = flags["--description"];
|
|
1017
|
+
if (desc.length > MAX_DESCRIPTION_LENGTH) {
|
|
1018
|
+
return `Description must be ${MAX_DESCRIPTION_LENGTH} characters or less.`;
|
|
1019
|
+
}
|
|
1020
|
+
route.description = desc || void 0;
|
|
1021
|
+
}
|
|
1022
|
+
if (flags["--src"] !== void 0) {
|
|
1023
|
+
route.route.src = stripQuotes(flags["--src"]);
|
|
1024
|
+
}
|
|
1025
|
+
if (flags["--src-syntax"] !== void 0) {
|
|
1026
|
+
const syntax = flags["--src-syntax"];
|
|
1027
|
+
if (!VALID_SYNTAXES.includes(syntax)) {
|
|
1028
|
+
return `Invalid syntax: "${syntax}". Valid options: ${VALID_SYNTAXES.join(", ")}`;
|
|
1029
|
+
}
|
|
1030
|
+
route.srcSyntax = syntax;
|
|
1031
|
+
}
|
|
1032
|
+
const actionFlag = flags["--action"];
|
|
1033
|
+
const destFlag = flags["--dest"];
|
|
1034
|
+
const statusFlag = flags["--status"];
|
|
1035
|
+
const noDest = flags["--no-dest"];
|
|
1036
|
+
const noStatus = flags["--no-status"];
|
|
1037
|
+
if (actionFlag) {
|
|
1038
|
+
if (!VALID_ACTION_TYPES.includes(actionFlag)) {
|
|
1039
|
+
return `Invalid action type: "${actionFlag}". Valid types: ${VALID_ACTION_TYPES.join(", ")}`;
|
|
1040
|
+
}
|
|
1041
|
+
switch (actionFlag) {
|
|
1042
|
+
case "rewrite": {
|
|
1043
|
+
const dest = destFlag ? stripQuotes(destFlag) : void 0;
|
|
1044
|
+
if (!dest)
|
|
1045
|
+
return "--action rewrite requires --dest.";
|
|
1046
|
+
route.route.dest = dest;
|
|
1047
|
+
delete route.route.status;
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
case "redirect": {
|
|
1051
|
+
const dest = destFlag ? stripQuotes(destFlag) : void 0;
|
|
1052
|
+
if (!dest)
|
|
1053
|
+
return "--action redirect requires --dest.";
|
|
1054
|
+
if (statusFlag === void 0)
|
|
1055
|
+
return `--action redirect requires --status (${REDIRECT_STATUS_CODES.join(", ")}).`;
|
|
1056
|
+
if (!REDIRECT_STATUS_CODES.includes(statusFlag))
|
|
1057
|
+
return `Invalid redirect status: ${statusFlag}. Must be one of: ${REDIRECT_STATUS_CODES.join(", ")}`;
|
|
1058
|
+
route.route.dest = dest;
|
|
1059
|
+
route.route.status = statusFlag;
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1062
|
+
case "set-status": {
|
|
1063
|
+
if (statusFlag === void 0)
|
|
1064
|
+
return "--action set-status requires --status.";
|
|
1065
|
+
if (statusFlag < 100 || statusFlag > 599)
|
|
1066
|
+
return "Status code must be between 100 and 599.";
|
|
1067
|
+
delete route.route.dest;
|
|
1068
|
+
route.route.status = statusFlag;
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
} else {
|
|
1073
|
+
if (destFlag !== void 0) {
|
|
1074
|
+
route.route.dest = stripQuotes(destFlag);
|
|
1075
|
+
}
|
|
1076
|
+
if (statusFlag !== void 0) {
|
|
1077
|
+
route.route.status = statusFlag;
|
|
1078
|
+
}
|
|
1079
|
+
if (noDest) {
|
|
1080
|
+
delete route.route.dest;
|
|
1081
|
+
}
|
|
1082
|
+
if (noStatus) {
|
|
1083
|
+
delete route.route.status;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (flags["--clear-conditions"]) {
|
|
1087
|
+
route.route.has = [];
|
|
1088
|
+
route.route.missing = [];
|
|
1089
|
+
}
|
|
1090
|
+
if (flags["--clear-headers"]) {
|
|
1091
|
+
route.route.headers = {};
|
|
1092
|
+
}
|
|
1093
|
+
if (flags["--clear-transforms"]) {
|
|
1094
|
+
route.route.transforms = [];
|
|
1095
|
+
}
|
|
1096
|
+
const transformFlags = extractTransformFlags(flags);
|
|
1097
|
+
try {
|
|
1098
|
+
const { headers, transforms } = collectHeadersAndTransforms(transformFlags);
|
|
1099
|
+
if (Object.keys(headers).length > 0) {
|
|
1100
|
+
route.route.headers = {
|
|
1101
|
+
...route.route.headers ?? {},
|
|
1102
|
+
...headers
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
if (transforms.length > 0) {
|
|
1106
|
+
const existing = route.route.transforms ?? [];
|
|
1107
|
+
route.route.transforms = [...existing, ...transforms];
|
|
1108
|
+
}
|
|
1109
|
+
} catch (e) {
|
|
1110
|
+
return `Invalid transform format. ${e instanceof Error ? e.message : ""}`;
|
|
1111
|
+
}
|
|
1112
|
+
const hasFlags = flags["--has"];
|
|
1113
|
+
const missingFlags = flags["--missing"];
|
|
1114
|
+
try {
|
|
1115
|
+
if (hasFlags) {
|
|
1116
|
+
const newHas = parseConditions(hasFlags);
|
|
1117
|
+
const existingHas = route.route.has ?? [];
|
|
1118
|
+
route.route.has = [...existingHas, ...newHas];
|
|
1119
|
+
}
|
|
1120
|
+
if (missingFlags) {
|
|
1121
|
+
const newMissing = parseConditions(missingFlags);
|
|
1122
|
+
const existingMissing = route.route.missing ?? [];
|
|
1123
|
+
route.route.missing = [...existingMissing, ...newMissing];
|
|
1124
|
+
}
|
|
1125
|
+
} catch (e) {
|
|
1126
|
+
return e instanceof Error ? e.message : "Invalid condition format";
|
|
1127
|
+
}
|
|
1128
|
+
const totalConditions = (route.route.has ?? []).length + (route.route.missing ?? []).length;
|
|
1129
|
+
if (totalConditions > MAX_CONDITIONS) {
|
|
1130
|
+
return `Too many conditions: ${totalConditions}. Maximum is ${MAX_CONDITIONS}.`;
|
|
1131
|
+
}
|
|
1132
|
+
const hasDest = !!route.route.dest;
|
|
1133
|
+
const hasStatus = !!route.route.status;
|
|
1134
|
+
const hasHeaders = Object.keys(route.route.headers ?? {}).length > 0;
|
|
1135
|
+
const hasTransforms = (route.route.transforms ?? []).length > 0;
|
|
1136
|
+
if (!hasDest && !hasStatus && !hasHeaders && !hasTransforms) {
|
|
1137
|
+
return "This edit would leave the route with no action. Add --action, headers, or transforms.";
|
|
1138
|
+
}
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
async function runInteractiveEditLoop(client, route) {
|
|
1142
|
+
for (; ; ) {
|
|
1143
|
+
const hasConds = (route.route.has ?? []).length;
|
|
1144
|
+
const missingConds = (route.route.missing ?? []).length;
|
|
1145
|
+
const responseHeaders = getAllResponseHeaders(route).length;
|
|
1146
|
+
const requestHeaders = getTransformsByType(route, "request.headers").length;
|
|
1147
|
+
const requestQuery = getTransformsByType(route, "request.query").length;
|
|
1148
|
+
const syntaxLabel = route.srcSyntax === "path-to-regexp" ? "Pattern" : route.srcSyntax === "equals" ? "Exact" : "Regex";
|
|
1149
|
+
const descriptionPreview = route.description ? route.description.length > 40 ? route.description.slice(0, 40) + "..." : route.description : "";
|
|
1150
|
+
const editChoices = [
|
|
1151
|
+
{ name: `Name (${route.name})`, value: "name" },
|
|
1152
|
+
{
|
|
1153
|
+
name: descriptionPreview ? `Description (${descriptionPreview})` : "Description",
|
|
1154
|
+
value: "description"
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
name: `Source (${syntaxLabel}: ${route.route.src})`,
|
|
1158
|
+
value: "source"
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
name: `Primary action (${getPrimaryActionLabel(route)})`,
|
|
1162
|
+
value: "action"
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
name: `Conditions (${hasConds} has, ${missingConds} missing)`,
|
|
1166
|
+
value: "conditions"
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
name: `Response Headers (${responseHeaders})`,
|
|
1170
|
+
value: "response-headers"
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
name: `Request Headers (${requestHeaders})`,
|
|
1174
|
+
value: "request-headers"
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
name: `Request Query (${requestQuery})`,
|
|
1178
|
+
value: "request-query"
|
|
1179
|
+
},
|
|
1180
|
+
{ name: "Done - save changes", value: "done" }
|
|
1181
|
+
];
|
|
1182
|
+
const choice = await client.input.select({
|
|
1183
|
+
message: "What would you like to edit?",
|
|
1184
|
+
choices: editChoices,
|
|
1185
|
+
pageSize: editChoices.length,
|
|
1186
|
+
loop: false
|
|
1187
|
+
});
|
|
1188
|
+
switch (choice) {
|
|
1189
|
+
case "name":
|
|
1190
|
+
await editName(client, route);
|
|
1191
|
+
break;
|
|
1192
|
+
case "description":
|
|
1193
|
+
await editDescription(client, route);
|
|
1194
|
+
break;
|
|
1195
|
+
case "source":
|
|
1196
|
+
await editSource(client, route);
|
|
1197
|
+
break;
|
|
1198
|
+
case "action":
|
|
1199
|
+
await editPrimaryAction(client, route);
|
|
1200
|
+
break;
|
|
1201
|
+
case "conditions":
|
|
1202
|
+
await editConditions(client, route);
|
|
1203
|
+
break;
|
|
1204
|
+
case "response-headers":
|
|
1205
|
+
await editResponseHeaders(client, route);
|
|
1206
|
+
break;
|
|
1207
|
+
case "request-headers":
|
|
1208
|
+
await editTransformsByType(
|
|
1209
|
+
client,
|
|
1210
|
+
route,
|
|
1211
|
+
"request.headers",
|
|
1212
|
+
"request-header"
|
|
1213
|
+
);
|
|
1214
|
+
break;
|
|
1215
|
+
case "request-query":
|
|
1216
|
+
await editTransformsByType(
|
|
1217
|
+
client,
|
|
1218
|
+
route,
|
|
1219
|
+
"request.query",
|
|
1220
|
+
"request-query"
|
|
1221
|
+
);
|
|
1222
|
+
break;
|
|
1223
|
+
case "done":
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
if (choice === "done") {
|
|
1227
|
+
const hasDest = !!route.route.dest;
|
|
1228
|
+
const hasStatus = !!route.route.status;
|
|
1229
|
+
const hasHeaders = Object.keys(route.route.headers ?? {}).length > 0;
|
|
1230
|
+
const hasTransforms = (route.route.transforms ?? []).length > 0;
|
|
1231
|
+
if (!hasDest && !hasStatus && !hasHeaders && !hasTransforms) {
|
|
1232
|
+
output_manager_default.warn(
|
|
1233
|
+
"Route has no action (no destination, status, or headers). Add an action before saving."
|
|
1234
|
+
);
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async function editName(client, route) {
|
|
1242
|
+
const name = await client.input.text({
|
|
1243
|
+
message: `Name (current: ${route.name}):`,
|
|
1244
|
+
validate: (val) => {
|
|
1245
|
+
if (!val)
|
|
1246
|
+
return "Route name is required";
|
|
1247
|
+
if (val.length > MAX_NAME_LENGTH)
|
|
1248
|
+
return `Name must be ${MAX_NAME_LENGTH} characters or less`;
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
route.name = name;
|
|
1253
|
+
}
|
|
1254
|
+
async function editDescription(client, route) {
|
|
1255
|
+
const desc = await client.input.text({
|
|
1256
|
+
message: `Description${route.description ? ` (current: ${route.description})` : ""}:`,
|
|
1257
|
+
validate: (val) => val && val.length > MAX_DESCRIPTION_LENGTH ? `Description must be ${MAX_DESCRIPTION_LENGTH} characters or less` : true
|
|
1258
|
+
});
|
|
1259
|
+
route.description = desc || void 0;
|
|
1260
|
+
}
|
|
1261
|
+
async function editSource(client, route) {
|
|
1262
|
+
const syntaxChoice = await client.input.select({
|
|
1263
|
+
message: `Path syntax (current: ${route.srcSyntax ?? "regex"}):`,
|
|
1264
|
+
choices: [
|
|
1265
|
+
{
|
|
1266
|
+
name: "Path pattern (e.g., /api/:version/users/:id)",
|
|
1267
|
+
value: "path-to-regexp"
|
|
1268
|
+
},
|
|
1269
|
+
{ name: "Exact match (e.g., /about)", value: "equals" },
|
|
1270
|
+
{ name: "Regular expression (e.g., ^/api/(.*)$)", value: "regex" }
|
|
1271
|
+
]
|
|
1272
|
+
});
|
|
1273
|
+
route.srcSyntax = syntaxChoice;
|
|
1274
|
+
const src = await client.input.text({
|
|
1275
|
+
message: `Path pattern (current: ${route.route.src}):`,
|
|
1276
|
+
validate: (val) => {
|
|
1277
|
+
if (!val)
|
|
1278
|
+
return "Path pattern is required";
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
route.route.src = src;
|
|
1283
|
+
}
|
|
1284
|
+
async function editPrimaryAction(client, route) {
|
|
1285
|
+
const currentType = getPrimaryActionType(route);
|
|
1286
|
+
const choices = [];
|
|
1287
|
+
if (currentType === "rewrite" || currentType === "redirect") {
|
|
1288
|
+
choices.push({ name: "Change destination", value: "change-dest" });
|
|
1289
|
+
}
|
|
1290
|
+
if (currentType === "redirect" || currentType === "set-status") {
|
|
1291
|
+
choices.push({ name: "Change status code", value: "change-status" });
|
|
1292
|
+
}
|
|
1293
|
+
if (currentType !== "rewrite") {
|
|
1294
|
+
choices.push({ name: "Switch to Rewrite", value: "switch-rewrite" });
|
|
1295
|
+
}
|
|
1296
|
+
if (currentType !== "redirect") {
|
|
1297
|
+
choices.push({ name: "Switch to Redirect", value: "switch-redirect" });
|
|
1298
|
+
}
|
|
1299
|
+
if (currentType !== "set-status") {
|
|
1300
|
+
choices.push({
|
|
1301
|
+
name: "Switch to Set Status Code",
|
|
1302
|
+
value: "switch-set-status"
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
if (currentType) {
|
|
1306
|
+
choices.push({
|
|
1307
|
+
name: "Remove primary action",
|
|
1308
|
+
value: "remove"
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
choices.push({ name: "Add Rewrite", value: "switch-rewrite" });
|
|
1312
|
+
choices.push({ name: "Add Redirect", value: "switch-redirect" });
|
|
1313
|
+
choices.push({ name: "Add Set Status Code", value: "switch-set-status" });
|
|
1314
|
+
}
|
|
1315
|
+
choices.push({ name: "Back", value: "back" });
|
|
1316
|
+
const action = await client.input.select({
|
|
1317
|
+
message: `Primary action (current: ${getPrimaryActionLabel(route)}):`,
|
|
1318
|
+
choices
|
|
1319
|
+
});
|
|
1320
|
+
const flags = {};
|
|
1321
|
+
switch (action) {
|
|
1322
|
+
case "change-dest": {
|
|
1323
|
+
const dest = await client.input.text({
|
|
1324
|
+
message: `Destination (current: ${route.route.dest}):`,
|
|
1325
|
+
validate: (val) => val ? true : "Destination is required"
|
|
1326
|
+
});
|
|
1327
|
+
route.route.dest = dest;
|
|
1328
|
+
break;
|
|
1329
|
+
}
|
|
1330
|
+
case "change-status": {
|
|
1331
|
+
if (currentType === "redirect") {
|
|
1332
|
+
const status = await client.input.select({
|
|
1333
|
+
message: `Status code (current: ${route.route.status}):`,
|
|
1334
|
+
choices: [
|
|
1335
|
+
{ name: "307 - Temporary Redirect", value: 307 },
|
|
1336
|
+
{ name: "308 - Permanent Redirect", value: 308 },
|
|
1337
|
+
{ name: "301 - Moved Permanently", value: 301 },
|
|
1338
|
+
{ name: "302 - Found", value: 302 },
|
|
1339
|
+
{ name: "303 - See Other", value: 303 }
|
|
1340
|
+
]
|
|
1341
|
+
});
|
|
1342
|
+
route.route.status = status;
|
|
1343
|
+
} else {
|
|
1344
|
+
const statusCode = await client.input.text({
|
|
1345
|
+
message: `Status code (current: ${route.route.status}):`,
|
|
1346
|
+
validate: (val) => {
|
|
1347
|
+
const num = parseInt(val, 10);
|
|
1348
|
+
if (isNaN(num) || num < 100 || num > 599) {
|
|
1349
|
+
return "Status code must be between 100 and 599";
|
|
1350
|
+
}
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
route.route.status = parseInt(statusCode, 10);
|
|
1355
|
+
}
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
case "switch-rewrite": {
|
|
1359
|
+
await collectActionDetails(client, "rewrite", flags);
|
|
1360
|
+
route.route.dest = flags["--dest"];
|
|
1361
|
+
delete route.route.status;
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
case "switch-redirect": {
|
|
1365
|
+
await collectActionDetails(client, "redirect", flags);
|
|
1366
|
+
route.route.dest = flags["--dest"];
|
|
1367
|
+
route.route.status = flags["--status"];
|
|
1368
|
+
break;
|
|
1369
|
+
}
|
|
1370
|
+
case "switch-set-status": {
|
|
1371
|
+
await collectActionDetails(client, "set-status", flags);
|
|
1372
|
+
delete route.route.dest;
|
|
1373
|
+
route.route.status = flags["--status"];
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
case "remove": {
|
|
1377
|
+
delete route.route.dest;
|
|
1378
|
+
delete route.route.status;
|
|
1379
|
+
break;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
async function editConditions(client, route) {
|
|
1384
|
+
for (; ; ) {
|
|
1385
|
+
const hasConds = route.route.has ?? [];
|
|
1386
|
+
const missingConds = route.route.missing ?? [];
|
|
1387
|
+
if (hasConds.length > 0 || missingConds.length > 0) {
|
|
1388
|
+
output_manager_default.print("\n");
|
|
1389
|
+
if (hasConds.length > 0) {
|
|
1390
|
+
output_manager_default.print(` ${import_chalk2.default.cyan("Has conditions:")}
|
|
1391
|
+
`);
|
|
1392
|
+
hasConds.forEach((c, i) => {
|
|
1393
|
+
output_manager_default.print(
|
|
1394
|
+
` ${import_chalk2.default.gray(`${i + 1}.`)} ${formatCondition(c)}
|
|
1395
|
+
`
|
|
1396
|
+
);
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
if (missingConds.length > 0) {
|
|
1400
|
+
output_manager_default.print(` ${import_chalk2.default.cyan("Does not have conditions:")}
|
|
1401
|
+
`);
|
|
1402
|
+
missingConds.forEach((c, i) => {
|
|
1403
|
+
output_manager_default.print(
|
|
1404
|
+
` ${import_chalk2.default.gray(`${hasConds.length + i + 1}.`)} ${formatCondition(c)}
|
|
1405
|
+
`
|
|
1406
|
+
);
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
output_manager_default.print("\n");
|
|
1410
|
+
} else {
|
|
1411
|
+
output_manager_default.print("\n No conditions set.\n\n");
|
|
1412
|
+
}
|
|
1413
|
+
const choices = [];
|
|
1414
|
+
if (hasConds.length > 0 || missingConds.length > 0) {
|
|
1415
|
+
choices.push({ name: "Remove a condition", value: "remove" });
|
|
1416
|
+
}
|
|
1417
|
+
choices.push({ name: "Add a new condition", value: "add" });
|
|
1418
|
+
choices.push({ name: "Back", value: "back" });
|
|
1419
|
+
const action = await client.input.select({
|
|
1420
|
+
message: "Conditions:",
|
|
1421
|
+
choices
|
|
1422
|
+
});
|
|
1423
|
+
if (action === "back")
|
|
1424
|
+
break;
|
|
1425
|
+
if (action === "remove") {
|
|
1426
|
+
const allConds = [
|
|
1427
|
+
...hasConds.map((c, i) => ({
|
|
1428
|
+
label: `[has] ${formatCondition(c)}`,
|
|
1429
|
+
idx: i,
|
|
1430
|
+
kind: "has"
|
|
1431
|
+
})),
|
|
1432
|
+
...missingConds.map((c, i) => ({
|
|
1433
|
+
label: `[does not have] ${formatCondition(c)}`,
|
|
1434
|
+
idx: i,
|
|
1435
|
+
kind: "missing"
|
|
1436
|
+
}))
|
|
1437
|
+
];
|
|
1438
|
+
const toRemove = await client.input.select({
|
|
1439
|
+
message: "Select condition to remove:",
|
|
1440
|
+
choices: [
|
|
1441
|
+
...allConds.map((c, i) => ({
|
|
1442
|
+
name: c.label,
|
|
1443
|
+
value: i
|
|
1444
|
+
})),
|
|
1445
|
+
{ name: "Cancel", value: -1 }
|
|
1446
|
+
]
|
|
1447
|
+
});
|
|
1448
|
+
if (toRemove !== -1) {
|
|
1449
|
+
const selected = allConds[toRemove];
|
|
1450
|
+
if (selected.kind === "has") {
|
|
1451
|
+
hasConds.splice(selected.idx, 1);
|
|
1452
|
+
route.route.has = hasConds;
|
|
1453
|
+
} else {
|
|
1454
|
+
missingConds.splice(selected.idx, 1);
|
|
1455
|
+
route.route.missing = missingConds;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
if (action === "add") {
|
|
1460
|
+
const existingHasStrings = hasConds.map(
|
|
1461
|
+
(c) => formatCondition2(c)
|
|
1462
|
+
);
|
|
1463
|
+
const existingMissingStrings = missingConds.map(
|
|
1464
|
+
(c) => formatCondition2(c)
|
|
1465
|
+
);
|
|
1466
|
+
const tempFlags = {
|
|
1467
|
+
"--has": existingHasStrings.length > 0 ? existingHasStrings : void 0,
|
|
1468
|
+
"--missing": existingMissingStrings.length > 0 ? existingMissingStrings : void 0
|
|
1469
|
+
};
|
|
1470
|
+
const hasBefore = existingHasStrings.length;
|
|
1471
|
+
const missingBefore = existingMissingStrings.length;
|
|
1472
|
+
await collectInteractiveConditions(client, tempFlags);
|
|
1473
|
+
const allHas = tempFlags["--has"] || [];
|
|
1474
|
+
const allMissing = tempFlags["--missing"] || [];
|
|
1475
|
+
const newHas = allHas.slice(hasBefore);
|
|
1476
|
+
const newMissing = allMissing.slice(missingBefore);
|
|
1477
|
+
if (newHas.length > 0) {
|
|
1478
|
+
const parsed = parseConditions(newHas);
|
|
1479
|
+
const existing = route.route.has ?? [];
|
|
1480
|
+
route.route.has = [...existing, ...parsed];
|
|
1481
|
+
}
|
|
1482
|
+
if (newMissing.length > 0) {
|
|
1483
|
+
const parsed = parseConditions(newMissing);
|
|
1484
|
+
const existing = route.route.missing ?? [];
|
|
1485
|
+
route.route.missing = [...existing, ...parsed];
|
|
1486
|
+
}
|
|
1487
|
+
break;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
function getAllResponseHeaders(route) {
|
|
1492
|
+
const items = [];
|
|
1493
|
+
for (const [key, value] of Object.entries(route.route.headers ?? {})) {
|
|
1494
|
+
items.push({ op: "set", key, value, source: "headers" });
|
|
1495
|
+
}
|
|
1496
|
+
const transforms = route.route.transforms ?? [];
|
|
1497
|
+
for (const t of transforms) {
|
|
1498
|
+
if (t.type === "response.headers") {
|
|
1499
|
+
items.push({
|
|
1500
|
+
op: t.op,
|
|
1501
|
+
key: typeof t.target.key === "string" ? t.target.key : JSON.stringify(t.target.key),
|
|
1502
|
+
value: typeof t.args === "string" ? t.args : void 0,
|
|
1503
|
+
source: "transform"
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return items;
|
|
1508
|
+
}
|
|
1509
|
+
function formatResponseHeaderItem(item) {
|
|
1510
|
+
if (item.op === "delete") {
|
|
1511
|
+
return `${import_chalk2.default.yellow(item.op)} ${import_chalk2.default.cyan(item.key)}`;
|
|
1512
|
+
}
|
|
1513
|
+
return `${import_chalk2.default.yellow(item.op)} ${import_chalk2.default.cyan(item.key)} = ${item.value}`;
|
|
1514
|
+
}
|
|
1515
|
+
async function editResponseHeaders(client, route) {
|
|
1516
|
+
for (; ; ) {
|
|
1517
|
+
const allHeaders = getAllResponseHeaders(route);
|
|
1518
|
+
if (allHeaders.length > 0) {
|
|
1519
|
+
output_manager_default.print("\n");
|
|
1520
|
+
output_manager_default.print(` ${import_chalk2.default.cyan("Response Headers:")}
|
|
1521
|
+
`);
|
|
1522
|
+
allHeaders.forEach((h, i) => {
|
|
1523
|
+
output_manager_default.print(
|
|
1524
|
+
` ${import_chalk2.default.gray(`${i + 1}.`)} ${formatResponseHeaderItem(h)}
|
|
1525
|
+
`
|
|
1526
|
+
);
|
|
1527
|
+
});
|
|
1528
|
+
output_manager_default.print("\n");
|
|
1529
|
+
} else {
|
|
1530
|
+
output_manager_default.print("\n No response headers set.\n\n");
|
|
1531
|
+
}
|
|
1532
|
+
const choices = [];
|
|
1533
|
+
if (allHeaders.length > 0) {
|
|
1534
|
+
choices.push({ name: "Remove a response header", value: "remove" });
|
|
1535
|
+
}
|
|
1536
|
+
choices.push({ name: "Add a response header", value: "add" });
|
|
1537
|
+
choices.push({ name: "Back", value: "back" });
|
|
1538
|
+
const action = await client.input.select({
|
|
1539
|
+
message: "Response Headers:",
|
|
1540
|
+
choices
|
|
1541
|
+
});
|
|
1542
|
+
if (action === "back")
|
|
1543
|
+
break;
|
|
1544
|
+
if (action === "remove") {
|
|
1545
|
+
const toRemove = await client.input.select({
|
|
1546
|
+
message: "Select response header to remove:",
|
|
1547
|
+
choices: [
|
|
1548
|
+
...allHeaders.map((h, i) => ({
|
|
1549
|
+
name: h.op === "delete" ? `${h.op} ${h.key}` : `${h.op} ${h.key} = ${h.value}`,
|
|
1550
|
+
value: i
|
|
1551
|
+
})),
|
|
1552
|
+
{ name: "Cancel", value: -1 }
|
|
1553
|
+
]
|
|
1554
|
+
});
|
|
1555
|
+
if (toRemove !== -1) {
|
|
1556
|
+
const item = allHeaders[toRemove];
|
|
1557
|
+
if (item.source === "headers") {
|
|
1558
|
+
const currentHeaders = { ...route.route.headers ?? {} };
|
|
1559
|
+
delete currentHeaders[item.key];
|
|
1560
|
+
route.route.headers = currentHeaders;
|
|
1561
|
+
} else {
|
|
1562
|
+
const transforms = route.route.transforms ?? [];
|
|
1563
|
+
const idx = transforms.findIndex(
|
|
1564
|
+
(t) => t.type === "response.headers" && t.op === item.op && (typeof t.target.key === "string" ? t.target.key : JSON.stringify(t.target.key)) === item.key
|
|
1565
|
+
);
|
|
1566
|
+
if (idx !== -1) {
|
|
1567
|
+
transforms.splice(idx, 1);
|
|
1568
|
+
route.route.transforms = transforms;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
if (action === "add") {
|
|
1574
|
+
const tempFlags = {};
|
|
1575
|
+
await collectInteractiveHeaders(client, "response", tempFlags);
|
|
1576
|
+
const setHeaders = tempFlags["--set-response-header"] || [];
|
|
1577
|
+
for (const h of setHeaders) {
|
|
1578
|
+
const eqIdx = h.indexOf("=");
|
|
1579
|
+
if (eqIdx !== -1) {
|
|
1580
|
+
const key = h.slice(0, eqIdx).trim();
|
|
1581
|
+
const value = h.slice(eqIdx + 1);
|
|
1582
|
+
route.route.headers = {
|
|
1583
|
+
...route.route.headers ?? {},
|
|
1584
|
+
[key]: value
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
const appendHeaders = tempFlags["--append-response-header"] || [];
|
|
1589
|
+
const deleteHeaders = tempFlags["--delete-response-header"] || [];
|
|
1590
|
+
const existing = route.route.transforms ?? [];
|
|
1591
|
+
const newTransforms = [];
|
|
1592
|
+
for (const h of appendHeaders) {
|
|
1593
|
+
const eqIdx = h.indexOf("=");
|
|
1594
|
+
if (eqIdx !== -1) {
|
|
1595
|
+
newTransforms.push({
|
|
1596
|
+
type: "response.headers",
|
|
1597
|
+
op: "append",
|
|
1598
|
+
target: { key: h.slice(0, eqIdx).trim() },
|
|
1599
|
+
args: h.slice(eqIdx + 1)
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
for (const key of deleteHeaders) {
|
|
1604
|
+
newTransforms.push({
|
|
1605
|
+
type: "response.headers",
|
|
1606
|
+
op: "delete",
|
|
1607
|
+
target: { key: key.trim() }
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
if (newTransforms.length > 0) {
|
|
1611
|
+
route.route.transforms = [...existing, ...newTransforms];
|
|
1612
|
+
}
|
|
1613
|
+
break;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
async function editTransformsByType(client, route, transformType, headerType) {
|
|
1618
|
+
const label = transformType === "request.headers" ? "Request Headers" : "Request Query";
|
|
1619
|
+
const itemName = transformType === "request.headers" ? "request header" : "query parameter";
|
|
1620
|
+
for (; ; ) {
|
|
1621
|
+
const allTransforms = route.route.transforms ?? [];
|
|
1622
|
+
const matching = allTransforms.filter((t) => t.type === transformType);
|
|
1623
|
+
if (matching.length > 0) {
|
|
1624
|
+
output_manager_default.print("\n");
|
|
1625
|
+
output_manager_default.print(` ${import_chalk2.default.cyan(`${label}:`)}
|
|
1626
|
+
`);
|
|
1627
|
+
matching.forEach((t, i) => {
|
|
1628
|
+
output_manager_default.print(
|
|
1629
|
+
` ${import_chalk2.default.gray(`${i + 1}.`)} ${formatTransform(t)}
|
|
1630
|
+
`
|
|
1631
|
+
);
|
|
1632
|
+
});
|
|
1633
|
+
output_manager_default.print("\n");
|
|
1634
|
+
} else {
|
|
1635
|
+
output_manager_default.print(`
|
|
1636
|
+
No ${label.toLowerCase()} set.
|
|
1637
|
+
|
|
1638
|
+
`);
|
|
1639
|
+
}
|
|
1640
|
+
const choices = [];
|
|
1641
|
+
if (matching.length > 0) {
|
|
1642
|
+
choices.push({ name: `Remove a ${itemName}`, value: "remove" });
|
|
1643
|
+
}
|
|
1644
|
+
choices.push({ name: `Add a ${itemName}`, value: "add" });
|
|
1645
|
+
choices.push({ name: "Back", value: "back" });
|
|
1646
|
+
const action = await client.input.select({
|
|
1647
|
+
message: `${label}:`,
|
|
1648
|
+
choices
|
|
1649
|
+
});
|
|
1650
|
+
if (action === "back")
|
|
1651
|
+
break;
|
|
1652
|
+
if (action === "remove") {
|
|
1653
|
+
const toRemove = await client.input.select({
|
|
1654
|
+
message: `Select ${itemName} to remove:`,
|
|
1655
|
+
choices: [
|
|
1656
|
+
...matching.map((t, i) => ({
|
|
1657
|
+
name: formatTransform(t),
|
|
1658
|
+
value: i
|
|
1659
|
+
})),
|
|
1660
|
+
{ name: "Cancel", value: -1 }
|
|
1661
|
+
]
|
|
1662
|
+
});
|
|
1663
|
+
if (toRemove !== -1) {
|
|
1664
|
+
let matchIdx = 0;
|
|
1665
|
+
const removeIdx = allTransforms.findIndex((t) => {
|
|
1666
|
+
if (t.type === transformType) {
|
|
1667
|
+
if (matchIdx === toRemove)
|
|
1668
|
+
return true;
|
|
1669
|
+
matchIdx++;
|
|
1670
|
+
}
|
|
1671
|
+
return false;
|
|
1672
|
+
});
|
|
1673
|
+
if (removeIdx !== -1) {
|
|
1674
|
+
allTransforms.splice(removeIdx, 1);
|
|
1675
|
+
route.route.transforms = allTransforms;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
if (action === "add") {
|
|
1680
|
+
const tempFlags = {};
|
|
1681
|
+
await collectInteractiveHeaders(client, headerType, tempFlags);
|
|
1682
|
+
const tfFlags = extractTransformFlags(tempFlags);
|
|
1683
|
+
const { transforms } = collectHeadersAndTransforms(tfFlags);
|
|
1684
|
+
if (transforms.length > 0) {
|
|
1685
|
+
route.route.transforms = [...allTransforms, ...transforms];
|
|
1686
|
+
}
|
|
1687
|
+
break;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
export {
|
|
1693
|
+
parseConditions,
|
|
1694
|
+
populateRouteEnv,
|
|
1695
|
+
generateRoute,
|
|
1696
|
+
MAX_NAME_LENGTH,
|
|
1697
|
+
MAX_DESCRIPTION_LENGTH,
|
|
1698
|
+
MAX_CONDITIONS,
|
|
1699
|
+
VALID_SYNTAXES,
|
|
1700
|
+
REDIRECT_STATUS_CODES,
|
|
1701
|
+
ALL_ACTION_CHOICES,
|
|
1702
|
+
stripQuotes,
|
|
1703
|
+
extractTransformFlags,
|
|
1704
|
+
collectHeadersAndTransforms,
|
|
1705
|
+
hasAnyTransformFlags,
|
|
1706
|
+
validateActionFlags,
|
|
1707
|
+
collectActionDetails,
|
|
1708
|
+
collectInteractiveConditions,
|
|
1709
|
+
generatedRouteToAddInput,
|
|
1710
|
+
convertRouteToCurrentRoute,
|
|
1711
|
+
routingRuleToCurrentRoute,
|
|
1712
|
+
printGeneratedRoutePreview,
|
|
1713
|
+
printRouteConfig,
|
|
1714
|
+
cloneRoute,
|
|
1715
|
+
applyFlagMutations,
|
|
1716
|
+
runInteractiveEditLoop
|
|
1717
|
+
};
|