@andocorp/cli 0.1.3 → 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.
@@ -0,0 +1,223 @@
1
+ import { getStringFlag, hasFlag } from "./args.js";
2
+ const PUSH_SEGMENT = Symbol("push");
3
+ export function flagValue(parsedArgs, longName, shortName) {
4
+ if (!hasFlag(parsedArgs, longName, shortName)) {
5
+ return undefined;
6
+ }
7
+ const value = getStringFlag(parsedArgs, longName, shortName);
8
+ if (value == null || value.trim() === "") {
9
+ throw new Error(shortName == null
10
+ ? `--${longName} requires a value.`
11
+ : `--${longName}/-${shortName} requires a value.`);
12
+ }
13
+ return value;
14
+ }
15
+ function splitAtDelimiter(value, delimiter, index) {
16
+ return [value.slice(0, index), value.slice(index + delimiter.length)];
17
+ }
18
+ function findInlineDelimiter(value) {
19
+ const delimiters = [":=", "==", ":", "="];
20
+ let selected = null;
21
+ for (const delimiter of delimiters) {
22
+ const index = value.indexOf(delimiter);
23
+ if (index < 0) {
24
+ continue;
25
+ }
26
+ if (selected == null ||
27
+ index < selected.index ||
28
+ (index === selected.index && delimiter.length > selected.delimiter.length)) {
29
+ selected = {
30
+ delimiter,
31
+ index,
32
+ };
33
+ }
34
+ }
35
+ return selected;
36
+ }
37
+ function parseJsonLiteral(rawValue, label) {
38
+ try {
39
+ return JSON.parse(rawValue);
40
+ }
41
+ catch {
42
+ throw new Error(`${label} must be valid JSON.`);
43
+ }
44
+ }
45
+ export function parseInlineInputs(tokens) {
46
+ const bodyAssignments = [];
47
+ const headers = {};
48
+ const query = new URLSearchParams();
49
+ for (const token of tokens) {
50
+ const inlineDelimiter = findInlineDelimiter(token);
51
+ if (inlineDelimiter == null) {
52
+ throw new Error(`Could not parse API argument "${token}". Use name==value for query, Header:Value for headers, or field=value for JSON body fields.`);
53
+ }
54
+ const [name, value] = splitAtDelimiter(token, inlineDelimiter.delimiter, inlineDelimiter.index);
55
+ if (inlineDelimiter.delimiter === ":=") {
56
+ bodyAssignments.push({
57
+ fieldPath: name,
58
+ value: parseJsonLiteral(value, `${name}:=`),
59
+ });
60
+ continue;
61
+ }
62
+ if (inlineDelimiter.delimiter === "==") {
63
+ query.append(name, value);
64
+ continue;
65
+ }
66
+ if (inlineDelimiter.delimiter === ":" && /^[A-Za-z0-9-]+$/.test(name)) {
67
+ headers[name] = value;
68
+ continue;
69
+ }
70
+ if (inlineDelimiter.delimiter === "=") {
71
+ bodyAssignments.push({ fieldPath: name, value });
72
+ continue;
73
+ }
74
+ throw new Error(`Could not parse API argument "${token}". Use name==value for query, Header:Value for headers, or field=value for JSON body fields.`);
75
+ }
76
+ return {
77
+ bodyAssignments,
78
+ headers,
79
+ query,
80
+ };
81
+ }
82
+ function parseBodyPath(path) {
83
+ const segments = [];
84
+ let current = "";
85
+ for (let index = 0; index < path.length; index += 1) {
86
+ const character = path[index];
87
+ if (character === ".") {
88
+ if (current !== "") {
89
+ segments.push(current);
90
+ current = "";
91
+ }
92
+ continue;
93
+ }
94
+ if (character === "[") {
95
+ if (current !== "") {
96
+ segments.push(current);
97
+ current = "";
98
+ }
99
+ const closingIndex = path.indexOf("]", index + 1);
100
+ if (closingIndex < 0) {
101
+ throw new Error(`Invalid body field path "${path}".`);
102
+ }
103
+ const bracketSegment = path.slice(index + 1, closingIndex);
104
+ if (bracketSegment === "") {
105
+ segments.push(PUSH_SEGMENT);
106
+ }
107
+ else if (/^\d+$/.test(bracketSegment)) {
108
+ segments.push(Number.parseInt(bracketSegment, 10));
109
+ }
110
+ else {
111
+ segments.push(bracketSegment);
112
+ }
113
+ index = closingIndex;
114
+ continue;
115
+ }
116
+ current += character;
117
+ }
118
+ if (current !== "") {
119
+ segments.push(current);
120
+ }
121
+ if (segments.length === 0) {
122
+ throw new Error("Body field path cannot be empty.");
123
+ }
124
+ return segments;
125
+ }
126
+ function createPathContainer(nextSegment) {
127
+ return nextSegment === PUSH_SEGMENT || typeof nextSegment === "number" ? [] : {};
128
+ }
129
+ function isJsonContainer(value) {
130
+ return typeof value === "object" && value !== null;
131
+ }
132
+ function setArrayBodyPath(params) {
133
+ if (params.isLast) {
134
+ if (params.segment === PUSH_SEGMENT) {
135
+ params.current.push(params.value);
136
+ return params.current;
137
+ }
138
+ params.current[params.segment] = params.value;
139
+ return params.current;
140
+ }
141
+ const container = createPathContainer(params.nextSegment);
142
+ if (params.segment === PUSH_SEGMENT) {
143
+ params.current.push(container);
144
+ return container;
145
+ }
146
+ const existing = params.current[params.segment];
147
+ if (isJsonContainer(existing)) {
148
+ return existing;
149
+ }
150
+ params.current[params.segment] = container;
151
+ return container;
152
+ }
153
+ function setObjectBodyPath(params) {
154
+ if (params.isLast) {
155
+ params.current[params.segment] = params.value;
156
+ return params.current;
157
+ }
158
+ const existing = params.current[params.segment];
159
+ if (isJsonContainer(existing)) {
160
+ return existing;
161
+ }
162
+ const container = createPathContainer(params.nextSegment);
163
+ params.current[params.segment] = container;
164
+ return container;
165
+ }
166
+ function setBodyPath(target, fieldPath, value) {
167
+ const segments = parseBodyPath(fieldPath);
168
+ let current = target;
169
+ for (let index = 0; index < segments.length; index += 1) {
170
+ const segment = segments[index];
171
+ const nextSegment = segments[index + 1];
172
+ const isLast = index === segments.length - 1;
173
+ if (segment == null) {
174
+ throw new Error(`Invalid body field path "${fieldPath}".`);
175
+ }
176
+ if (Array.isArray(current)) {
177
+ if (typeof segment === "string") {
178
+ throw new Error(`Invalid object key in array body field path "${fieldPath}".`);
179
+ }
180
+ current = setArrayBodyPath({
181
+ current,
182
+ fieldPath,
183
+ isLast,
184
+ nextSegment,
185
+ segment,
186
+ value,
187
+ });
188
+ continue;
189
+ }
190
+ if (segment === PUSH_SEGMENT || typeof segment === "number") {
191
+ throw new Error(`Invalid array segment in body field path "${fieldPath}".`);
192
+ }
193
+ current = setObjectBodyPath({
194
+ current,
195
+ isLast,
196
+ nextSegment,
197
+ segment,
198
+ value,
199
+ });
200
+ }
201
+ }
202
+ export function buildBodyFromAssignments(assignments) {
203
+ const body = {};
204
+ for (const assignment of assignments) {
205
+ setBodyPath(body, assignment.fieldPath, assignment.value);
206
+ }
207
+ return body;
208
+ }
209
+ export async function readDataFlagBody(params) {
210
+ const data = flagValue(params.parsedArgs, "data", "d");
211
+ if (data == null) {
212
+ return undefined;
213
+ }
214
+ const rawBody = data === "-" ? await params.readStdin() : data;
215
+ return parseJsonLiteral(rawBody, "--data");
216
+ }
217
+ export async function defaultReadStdin() {
218
+ const chunks = [];
219
+ for await (const chunk of process.stdin) {
220
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
221
+ }
222
+ return Buffer.concat(chunks).toString("utf8");
223
+ }
@@ -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
+ }