@andocorp/cli 0.2.0 → 0.3.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 +21 -0
- package/README.md +83 -141
- package/dist/agent-commands.js +297 -0
- package/dist/api-command.js +187 -0
- package/dist/api-inputs.js +223 -0
- package/dist/api-operations.js +344 -0
- package/dist/args.js +71 -0
- package/dist/auth-commands.js +362 -0
- package/dist/cli-helpers.js +67 -0
- package/dist/cli-login-browser.js +60 -0
- package/dist/cli-login-errors.js +10 -0
- package/dist/cli-login-paths.js +8 -0
- package/dist/cli-login-revoke.js +100 -0
- package/dist/cli-login.js +335 -0
- package/dist/client.js +104 -0
- package/dist/commands.js +155 -0
- package/dist/config-credential-metadata.js +68 -0
- package/dist/config-keyring.js +61 -0
- package/dist/config-logout-credentials.js +171 -0
- package/dist/config-paths.js +41 -0
- package/dist/config-types.js +1 -0
- package/dist/config.js +333 -0
- package/dist/format.js +297 -0
- package/dist/help.js +70 -0
- package/dist/index.js +77 -34266
- package/dist/output.js +7 -0
- package/dist/session.js +58 -0
- package/dist/timeouts.js +1 -0
- package/dist/types.js +1 -0
- package/dist/watch-commands.js +120 -0
- package/package.json +15 -16
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { ANDO_PUBLIC_API_OPERATIONS, ANDO_PUBLIC_API_SPEC, resolveAndoPublicApiBaseUrl, } from "@andocorp/sdk";
|
|
2
|
+
import { hasFlag } from "./args.js";
|
|
3
|
+
import { printJson } from "./output.js";
|
|
4
|
+
const OPERATION_ENTRIES = Object.entries(ANDO_PUBLIC_API_OPERATIONS).map(([id, operation]) => ({
|
|
5
|
+
id: id,
|
|
6
|
+
operation,
|
|
7
|
+
}));
|
|
8
|
+
function normalizeApiPath(path) {
|
|
9
|
+
const withLeadingSlash = path.startsWith("/") ? path : `/${path}`;
|
|
10
|
+
const withoutTrailingSlash = withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/, "") : "/";
|
|
11
|
+
if (withoutTrailingSlash.startsWith("/api/v1/")) {
|
|
12
|
+
return withoutTrailingSlash.slice("/api/v1".length);
|
|
13
|
+
}
|
|
14
|
+
if (withoutTrailingSlash === "/api/v1") {
|
|
15
|
+
return "/";
|
|
16
|
+
}
|
|
17
|
+
if (withoutTrailingSlash.startsWith("/v1/")) {
|
|
18
|
+
return withoutTrailingSlash.slice("/v1".length);
|
|
19
|
+
}
|
|
20
|
+
return withoutTrailingSlash === "/v1" ? "/" : withoutTrailingSlash;
|
|
21
|
+
}
|
|
22
|
+
function parseApiPath(rawPath) {
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(rawPath);
|
|
25
|
+
return {
|
|
26
|
+
path: normalizeApiPath(url.pathname),
|
|
27
|
+
queryString: url.search.startsWith("?") ? url.search.slice(1) : url.search,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
const queryIndex = rawPath.indexOf("?");
|
|
32
|
+
if (queryIndex < 0) {
|
|
33
|
+
return {
|
|
34
|
+
path: normalizeApiPath(rawPath),
|
|
35
|
+
queryString: "",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
path: normalizeApiPath(rawPath.slice(0, queryIndex)),
|
|
40
|
+
queryString: rawPath.slice(queryIndex + 1),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function normalizeTemplate(path) {
|
|
45
|
+
return normalizeApiPath(path).replace(/:([A-Za-z0-9_]+)/g, "{$1}");
|
|
46
|
+
}
|
|
47
|
+
function splitRawQuery(rawTarget) {
|
|
48
|
+
const queryIndex = rawTarget.indexOf("?");
|
|
49
|
+
if (queryIndex < 0) {
|
|
50
|
+
return {
|
|
51
|
+
path: rawTarget,
|
|
52
|
+
queryString: "",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
path: rawTarget.slice(0, queryIndex),
|
|
57
|
+
queryString: rawTarget.slice(queryIndex + 1),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function operationPublicPath(operation) {
|
|
61
|
+
return operation.publicPath;
|
|
62
|
+
}
|
|
63
|
+
function matchTemplatePath(template, actualPath) {
|
|
64
|
+
const templateSegments = normalizeTemplate(template).split("/").filter(Boolean);
|
|
65
|
+
const actualSegments = normalizeApiPath(actualPath).split("/").filter(Boolean);
|
|
66
|
+
if (templateSegments.length !== actualSegments.length) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const parameters = {};
|
|
70
|
+
for (let index = 0; index < templateSegments.length; index += 1) {
|
|
71
|
+
const templateSegment = templateSegments[index];
|
|
72
|
+
const actualSegment = actualSegments[index];
|
|
73
|
+
if (templateSegment == null || actualSegment == null) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const parameterMatch = templateSegment.match(/^\{([A-Za-z0-9_]+)\}$/);
|
|
77
|
+
if (parameterMatch != null) {
|
|
78
|
+
const parameterName = parameterMatch[1];
|
|
79
|
+
if (parameterName == null) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
parameters[parameterName] = decodeURIComponent(actualSegment);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (templateSegment !== actualSegment) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return parameters;
|
|
90
|
+
}
|
|
91
|
+
function findOperationIdMatches(rawTarget) {
|
|
92
|
+
const parsedTarget = splitRawQuery(rawTarget);
|
|
93
|
+
if (parsedTarget.path.includes("/") || parsedTarget.path.includes(":")) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const entry = OPERATION_ENTRIES.find((operationEntry) => operationEntry.id === parsedTarget.path);
|
|
97
|
+
if (entry == null) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
...entry,
|
|
103
|
+
concretePath: normalizeApiPath(entry.operation.path),
|
|
104
|
+
pathParameters: {},
|
|
105
|
+
queryString: parsedTarget.queryString,
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
function findOperationMatches(rawPath) {
|
|
110
|
+
const operationIdMatches = findOperationIdMatches(rawPath);
|
|
111
|
+
if (operationIdMatches.length > 0) {
|
|
112
|
+
return operationIdMatches;
|
|
113
|
+
}
|
|
114
|
+
const parsedPath = parseApiPath(rawPath);
|
|
115
|
+
const matches = [];
|
|
116
|
+
for (const entry of OPERATION_ENTRIES) {
|
|
117
|
+
const templateMatch = matchTemplatePath(entry.operation.path, parsedPath.path);
|
|
118
|
+
if (templateMatch != null) {
|
|
119
|
+
matches.push({
|
|
120
|
+
...entry,
|
|
121
|
+
concretePath: parsedPath.path,
|
|
122
|
+
pathParameters: templateMatch,
|
|
123
|
+
queryString: parsedPath.queryString,
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (normalizeTemplate(operationPublicPath(entry.operation)) ===
|
|
128
|
+
normalizeTemplate(parsedPath.path)) {
|
|
129
|
+
matches.push({
|
|
130
|
+
...entry,
|
|
131
|
+
concretePath: normalizeApiPath(entry.operation.path),
|
|
132
|
+
pathParameters: {},
|
|
133
|
+
queryString: parsedPath.queryString,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return matches;
|
|
138
|
+
}
|
|
139
|
+
function formatOperationPath(template, pathParameters) {
|
|
140
|
+
let path = normalizeApiPath(template);
|
|
141
|
+
for (const [name, value] of Object.entries(pathParameters)) {
|
|
142
|
+
path = path.replace(`{${name}}`, encodeURIComponent(value));
|
|
143
|
+
}
|
|
144
|
+
return path;
|
|
145
|
+
}
|
|
146
|
+
export function matchingOperations(rawPath) {
|
|
147
|
+
const matches = findOperationMatches(rawPath);
|
|
148
|
+
if (matches.length === 0) {
|
|
149
|
+
throw new Error(`No public API operation matches ${rawPath}.`);
|
|
150
|
+
}
|
|
151
|
+
return matches;
|
|
152
|
+
}
|
|
153
|
+
export function selectOperation(rawPath, matches, method) {
|
|
154
|
+
const methodMatches = matches.filter((match) => match.operation.method === method);
|
|
155
|
+
if (methodMatches.length === 1) {
|
|
156
|
+
return methodMatches[0];
|
|
157
|
+
}
|
|
158
|
+
if (methodMatches.length > 1) {
|
|
159
|
+
throw new Error(`Multiple ${method} operations match ${rawPath}.`);
|
|
160
|
+
}
|
|
161
|
+
const availableMethods = [
|
|
162
|
+
...new Set(matches.map((match) => match.operation.method)),
|
|
163
|
+
].join(", ");
|
|
164
|
+
throw new Error(availableMethods === ""
|
|
165
|
+
? `No public API operation matches ${rawPath}.`
|
|
166
|
+
: `No ${method} operation matches ${rawPath}. Available methods: ${availableMethods}.`);
|
|
167
|
+
}
|
|
168
|
+
export function operationCanUseConcretePath(match) {
|
|
169
|
+
for (const parameterName of match.operation.pathParameters) {
|
|
170
|
+
const value = match.pathParameters[parameterName];
|
|
171
|
+
if (value == null ||
|
|
172
|
+
value === `{${parameterName}}` ||
|
|
173
|
+
value === `:${parameterName}`) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
function isPathParameterAssignment(match, assignment) {
|
|
180
|
+
return new Set(match.operation.pathParameters).has(assignment.fieldPath);
|
|
181
|
+
}
|
|
182
|
+
function pathParameterValue(assignment) {
|
|
183
|
+
const value = assignment.value;
|
|
184
|
+
if (typeof value === "string" ||
|
|
185
|
+
typeof value === "number" ||
|
|
186
|
+
typeof value === "boolean") {
|
|
187
|
+
return String(value);
|
|
188
|
+
}
|
|
189
|
+
throw new Error(`Path parameter ${assignment.fieldPath} must be a scalar value.`);
|
|
190
|
+
}
|
|
191
|
+
export function applyPathParameterAssignments(params) {
|
|
192
|
+
if (operationCanUseConcretePath(params.match)) {
|
|
193
|
+
return {
|
|
194
|
+
bodyAssignments: params.bodyAssignments,
|
|
195
|
+
match: params.match,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const bodyAssignments = [];
|
|
199
|
+
const pathParameters = { ...params.match.pathParameters };
|
|
200
|
+
for (const assignment of params.bodyAssignments) {
|
|
201
|
+
if (isPathParameterAssignment(params.match, assignment)) {
|
|
202
|
+
pathParameters[assignment.fieldPath] = pathParameterValue(assignment);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
bodyAssignments.push(assignment);
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
bodyAssignments,
|
|
209
|
+
match: {
|
|
210
|
+
...params.match,
|
|
211
|
+
concretePath: formatOperationPath(params.match.operation.path, pathParameters),
|
|
212
|
+
pathParameters,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function formatAllowed(values) {
|
|
217
|
+
return values.length === 0 ? "none" : values.join(", ");
|
|
218
|
+
}
|
|
219
|
+
function assertAllowedQueryParameters(match, queryNames) {
|
|
220
|
+
const allowed = new Set(match.operation.queryParameters);
|
|
221
|
+
for (const name of queryNames) {
|
|
222
|
+
if (!allowed.has(name)) {
|
|
223
|
+
throw new Error(`Unknown query parameter "${name}" for ${match.id}. Supported query parameters: ${formatAllowed(match.operation.queryParameters)}.`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function assertAllowedHeaderParameters(match, headerNames) {
|
|
228
|
+
const allowed = new Set(match.operation.headerParameters.map((headerName) => headerName.toLowerCase()));
|
|
229
|
+
for (const name of headerNames) {
|
|
230
|
+
if (!allowed.has(name.toLowerCase())) {
|
|
231
|
+
throw new Error(`Unknown header "${name}" for ${match.id}. Supported headers: ${formatAllowed(match.operation.headerParameters)}.`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
export function validateOperationInputs(params) {
|
|
236
|
+
if (params.body !== undefined && params.match.operation.requestBodySchema == null) {
|
|
237
|
+
throw new Error(`${params.match.id} does not accept a request body.`);
|
|
238
|
+
}
|
|
239
|
+
const pathQuery = new URLSearchParams(params.match.queryString);
|
|
240
|
+
assertAllowedQueryParameters(params.match, [
|
|
241
|
+
...pathQuery.keys(),
|
|
242
|
+
...params.inlineQuery.keys(),
|
|
243
|
+
]);
|
|
244
|
+
assertAllowedHeaderParameters(params.match, Object.keys(params.headers));
|
|
245
|
+
}
|
|
246
|
+
export function selectedMatches(matches, requestedMethod) {
|
|
247
|
+
if (requestedMethod == null) {
|
|
248
|
+
return matches;
|
|
249
|
+
}
|
|
250
|
+
const methodMatches = matches.filter((match) => match.operation.method === requestedMethod);
|
|
251
|
+
return methodMatches.length > 0 ? methodMatches : matches;
|
|
252
|
+
}
|
|
253
|
+
function applySearchParams(url, params) {
|
|
254
|
+
for (const [name, value] of params) {
|
|
255
|
+
url.searchParams.append(name, value);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
export function buildOperationUrl(params) {
|
|
259
|
+
const baseUrl = resolveAndoPublicApiBaseUrl(params.apiHost);
|
|
260
|
+
const url = new URL(`${baseUrl}${params.concretePath}`);
|
|
261
|
+
applySearchParams(url, new URLSearchParams(params.pathQueryString));
|
|
262
|
+
applySearchParams(url, params.inlineQuery);
|
|
263
|
+
return url.toString();
|
|
264
|
+
}
|
|
265
|
+
export function listOperations(parsedArgs) {
|
|
266
|
+
if (hasFlag(parsedArgs, "json")) {
|
|
267
|
+
printJson(OPERATION_ENTRIES.map(({ id, operation }) => ({
|
|
268
|
+
id,
|
|
269
|
+
method: operation.method,
|
|
270
|
+
path: operation.path,
|
|
271
|
+
public_path: operationPublicPath(operation),
|
|
272
|
+
ga_status: operation.gaStatus,
|
|
273
|
+
public_api_disposition: operation.publicApiDisposition,
|
|
274
|
+
request_body_schema: operation.requestBodySchema,
|
|
275
|
+
response_schema: operation.responseSchema,
|
|
276
|
+
})));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
process.stdout.write(OPERATION_ENTRIES.map(({ id, operation }) => [
|
|
280
|
+
operation.method,
|
|
281
|
+
operationPublicPath(operation),
|
|
282
|
+
id,
|
|
283
|
+
operation.publicApiDisposition,
|
|
284
|
+
].join("\t")).join("\n") + "\n");
|
|
285
|
+
}
|
|
286
|
+
export function printEndpointHelp(rawPath, matches) {
|
|
287
|
+
const lines = [`ando api ${rawPath}`, "", "Operations:"];
|
|
288
|
+
for (const { id, operation } of matches) {
|
|
289
|
+
lines.push(` ${operation.method.padEnd(6)} ${operationPublicPath(operation).padEnd(44)} ${id}`);
|
|
290
|
+
lines.push(` status: ${operation.publicApiDisposition}`);
|
|
291
|
+
lines.push(` path params: ${formatAllowed(operation.pathParameters)}`);
|
|
292
|
+
lines.push(` query params: ${formatAllowed(operation.queryParameters)}`);
|
|
293
|
+
lines.push(` headers: ${formatAllowed(operation.headerParameters)}`);
|
|
294
|
+
lines.push(` request body: ${operation.requestBodySchema ?? "none"}`);
|
|
295
|
+
lines.push(` response: ${operation.responseSchema}`);
|
|
296
|
+
}
|
|
297
|
+
lines.push("");
|
|
298
|
+
lines.push("Syntax:");
|
|
299
|
+
lines.push(" name==value query string parameter");
|
|
300
|
+
lines.push(" Header:Value declared request header");
|
|
301
|
+
lines.push(" field=value JSON body string field");
|
|
302
|
+
lines.push(" field:=json JSON body field parsed as JSON");
|
|
303
|
+
lines.push(" --data <json|-> complete JSON request body or stdin");
|
|
304
|
+
lines.push("");
|
|
305
|
+
lines.push("Targets can be public paths or operation IDs from `ando api ls`.");
|
|
306
|
+
lines.push("For operation ID targets, supply required path parameters as name=value.");
|
|
307
|
+
lines.push("");
|
|
308
|
+
lines.push(`Spec: ando api ${rawPath} --spec`);
|
|
309
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
310
|
+
}
|
|
311
|
+
export function buildOperationSpec(matches) {
|
|
312
|
+
const paths = {};
|
|
313
|
+
for (const { id, operation } of matches) {
|
|
314
|
+
const path = operation.path;
|
|
315
|
+
const method = operation.method.toLowerCase();
|
|
316
|
+
const pathItem = paths[path] ?? {};
|
|
317
|
+
pathItem[method] = {
|
|
318
|
+
operationId: id,
|
|
319
|
+
routeId: operation.routeId,
|
|
320
|
+
gaStatus: operation.gaStatus,
|
|
321
|
+
publicApiDisposition: operation.publicApiDisposition,
|
|
322
|
+
compatibilityMode: operation.compatibilityMode,
|
|
323
|
+
migrationTarget: operation.migrationTarget,
|
|
324
|
+
rateLimitPolicy: operation.rateLimitPolicy,
|
|
325
|
+
selectedRuntime: operation.selectedRuntime,
|
|
326
|
+
targetRuntime: operation.targetRuntime,
|
|
327
|
+
pathParameters: operation.pathParameters,
|
|
328
|
+
queryParameters: operation.queryParameters,
|
|
329
|
+
headerParameters: operation.headerParameters,
|
|
330
|
+
requestBodySchema: operation.requestBodySchema,
|
|
331
|
+
responseSchema: operation.responseSchema,
|
|
332
|
+
};
|
|
333
|
+
paths[path] = pathItem;
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
openapi: ANDO_PUBLIC_API_SPEC.openapi,
|
|
337
|
+
info: {
|
|
338
|
+
title: ANDO_PUBLIC_API_SPEC.title,
|
|
339
|
+
version: ANDO_PUBLIC_API_SPEC.version,
|
|
340
|
+
},
|
|
341
|
+
servers: ANDO_PUBLIC_API_SPEC.servers,
|
|
342
|
+
paths,
|
|
343
|
+
};
|
|
344
|
+
}
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
function isFlagValue(value) {
|
|
2
|
+
return value != null && (value === "-" || !value.startsWith("-"));
|
|
3
|
+
}
|
|
4
|
+
function setFlagWithOptionalValue(flags, name, argv, index) {
|
|
5
|
+
const nextValue = argv[index + 1];
|
|
6
|
+
if (isFlagValue(nextValue)) {
|
|
7
|
+
flags.set(name, nextValue);
|
|
8
|
+
return index + 1;
|
|
9
|
+
}
|
|
10
|
+
flags.set(name, true);
|
|
11
|
+
return index;
|
|
12
|
+
}
|
|
13
|
+
function parseLongFlag(flags, argv, index) {
|
|
14
|
+
const value = argv[index] ?? "";
|
|
15
|
+
const flag = value.slice(2);
|
|
16
|
+
const separatorIndex = flag.indexOf("=");
|
|
17
|
+
if (separatorIndex >= 0) {
|
|
18
|
+
flags.set(flag.slice(0, separatorIndex), flag.slice(separatorIndex + 1));
|
|
19
|
+
return index;
|
|
20
|
+
}
|
|
21
|
+
return setFlagWithOptionalValue(flags, flag, argv, index);
|
|
22
|
+
}
|
|
23
|
+
function parseShortFlag(flags, argv, index) {
|
|
24
|
+
const value = argv[index] ?? "";
|
|
25
|
+
const shortFlags = value.slice(1);
|
|
26
|
+
if (shortFlags.length <= 1) {
|
|
27
|
+
return setFlagWithOptionalValue(flags, shortFlags, argv, index);
|
|
28
|
+
}
|
|
29
|
+
for (const shortFlag of shortFlags.slice(0, -1)) {
|
|
30
|
+
flags.set(shortFlag, true);
|
|
31
|
+
}
|
|
32
|
+
return setFlagWithOptionalValue(flags, shortFlags.at(-1) ?? "", argv, index);
|
|
33
|
+
}
|
|
34
|
+
export function parseArgs(argv) {
|
|
35
|
+
const flags = new Map();
|
|
36
|
+
const positionals = [];
|
|
37
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
38
|
+
const value = argv[index];
|
|
39
|
+
if (value == null) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!value.startsWith("-")) {
|
|
43
|
+
positionals.push(value);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (value.startsWith("--")) {
|
|
47
|
+
index = parseLongFlag(flags, argv, index);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
index = parseShortFlag(flags, argv, index);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
flags,
|
|
54
|
+
positionals,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function getStringFlag(parsedArgs, longName, shortName) {
|
|
58
|
+
const longValue = parsedArgs.flags.get(longName);
|
|
59
|
+
if (typeof longValue === "string") {
|
|
60
|
+
return longValue;
|
|
61
|
+
}
|
|
62
|
+
if (shortName == null) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const shortValue = parsedArgs.flags.get(shortName);
|
|
66
|
+
return typeof shortValue === "string" ? shortValue : undefined;
|
|
67
|
+
}
|
|
68
|
+
export function hasFlag(parsedArgs, longName, shortName) {
|
|
69
|
+
return (parsedArgs.flags.has(longName) ||
|
|
70
|
+
(shortName != null && parsedArgs.flags.has(shortName)));
|
|
71
|
+
}
|