@smonn/ids 0.3.4 → 0.4.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/dist/cli.mjs CHANGED
@@ -1,38 +1,161 @@
1
1
  #!/usr/bin/env node
2
- import { t as createId } from "./id-CcoPE2EX.mjs";
3
- import { i as encodeOpaqueKey, n as importOpaqueKey, r as decodeOpaqueKey, t as createOpaqueId } from "./opaque-B-ttBfHO.mjs";
4
- //#region src/cli.ts
5
- async function run(opts) {
6
- const [subcommand, ...rest] = opts.argv;
7
- if (subcommand === "generate" || subcommand === "g") return runGenerate(rest, opts);
8
- if (subcommand === "inspect" || subcommand === "i") return runInspect(rest, opts);
9
- if (subcommand === "keygen" || subcommand === "k") return runKeygen(rest, opts);
10
- if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h") {
11
- opts.stdout(usage());
12
- return 0;
2
+ import { t as createTimestampId } from "./timestamp-BjdAetut.mjs";
3
+ import { i as encodeOpaqueKey, n as importOpaqueKey, r as decodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-CX-Lc5B9.mjs";
4
+ //#region src/cli/codec-options.ts
5
+ function codecOpts(opts) {
6
+ const o = { allowDuplicateBrand: true };
7
+ if (opts.now !== void 0) o.now = opts.now;
8
+ if (opts.rng !== void 0) o.rng = opts.rng;
9
+ return o;
10
+ }
11
+ //#endregion
12
+ //#region src/cli/constants.ts
13
+ const maxGenerateCount = 1e4;
14
+ //#endregion
15
+ //#region src/cli/flags.ts
16
+ function splitFlagToken(arg) {
17
+ const eq = arg.indexOf("=");
18
+ if (eq <= 0) return {
19
+ flag: arg,
20
+ inlineValue: void 0
21
+ };
22
+ return {
23
+ flag: arg.slice(0, eq),
24
+ inlineValue: arg.slice(eq + 1)
25
+ };
26
+ }
27
+ function splitFlags(args) {
28
+ const flags = /* @__PURE__ */ new Set();
29
+ const values = /* @__PURE__ */ new Map();
30
+ const positionals = [];
31
+ const errors = [];
32
+ const seenFlags = /* @__PURE__ */ new Set();
33
+ const valueFlags = new Set([
34
+ "--count",
35
+ "-c",
36
+ "--bits",
37
+ "--key-format"
38
+ ]);
39
+ const addFlag = (flag) => {
40
+ const canonical = canonicalFlag(flag);
41
+ if (seenFlags.has(canonical)) errors.push(`duplicate flag: ${canonical}`);
42
+ seenFlags.add(canonical);
43
+ flags.add(flag);
44
+ };
45
+ for (let i = 0; i < args.length; i++) {
46
+ const raw = args[i];
47
+ const { flag, inlineValue } = splitFlagToken(raw);
48
+ if (flag === "--opaque") {
49
+ addFlag(flag);
50
+ if (inlineValue !== void 0) errors.push("flag does not take a value: --opaque");
51
+ continue;
52
+ }
53
+ if (valueFlags.has(flag)) {
54
+ if (inlineValue !== void 0) {
55
+ addFlag(flag);
56
+ values.set(flag, inlineValue);
57
+ continue;
58
+ }
59
+ const value = args[i + 1];
60
+ if (value === void 0 || value.startsWith("-")) {
61
+ addFlag(flag);
62
+ values.set(flag, "");
63
+ continue;
64
+ }
65
+ addFlag(flag);
66
+ values.set(flag, value);
67
+ i++;
68
+ continue;
69
+ }
70
+ if (flag.startsWith("-")) {
71
+ addFlag(flag);
72
+ continue;
73
+ }
74
+ positionals.push(raw);
13
75
  }
14
- opts.stderr(usage());
15
- return 1;
76
+ return {
77
+ flags,
78
+ values,
79
+ positionals,
80
+ errors
81
+ };
16
82
  }
17
- function usage() {
18
- return [
19
- "Usage: ids <subcommand> [args]",
20
- "",
21
- "Subcommands:",
22
- " inspect, i <id> [--opaque] [--key-format hex|base64url]",
23
- " Decode an ID and print brand, timestamp, and canonical form.",
24
- " --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
25
- " generate, g <brand> [--count, -c N] [--opaque] [--key-format hex|base64url]",
26
- " Mint one or more canonical IDs for the given brand.",
27
- " --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
28
- " keygen, k [--bits 128|192|256] [--key-format hex|base64url]",
29
- " Emit a random AES key for importOpaqueKey (stdout only).",
30
- ""
31
- ].join("\n");
83
+ function canonicalFlag(flag) {
84
+ if (flag === "-c") return "--count";
85
+ return flag;
32
86
  }
33
- function runInspect(args, opts) {
87
+ const knownFlags = new Set([
88
+ "--opaque",
89
+ "--key-format",
90
+ "--count",
91
+ "-c",
92
+ "--bits"
93
+ ]);
94
+ function unsupportedFlagForCommand(command, flags, allowed) {
95
+ for (const flag of flags) if (!allowed.has(flag)) return knownFlags.has(flag) ? `unsupported flag for ${command}: ${flag}` : `unsupported flag: ${flag}`;
96
+ }
97
+ function parseCount(values) {
98
+ const raw = values.get("--count") ?? values.get("-c");
99
+ if (raw === void 0) return 1;
100
+ if (raw === "") return "--count requires a value";
101
+ if (!/^[1-9][0-9]*$/.test(raw)) return `--count must be a positive integer, got '${raw}'`;
102
+ const count = Number.parseInt(raw, 10);
103
+ if (!Number.isSafeInteger(count) || count > 1e4) return `--count must be at most ${maxGenerateCount}, got '${raw}'`;
104
+ return count;
105
+ }
106
+ function parseBits(values) {
107
+ const raw = values.get("--bits");
108
+ if (raw === void 0) return 256;
109
+ if (raw === "") return "--bits requires a value";
110
+ if (raw === "128") return 128;
111
+ if (raw === "192") return 192;
112
+ if (raw === "256") return 256;
113
+ return `--bits must be 128, 192, or 256, got '${raw}'`;
114
+ }
115
+ //#endregion
116
+ //#region src/cli/opaque-key.ts
117
+ function isKeyFormatError(result) {
118
+ return result !== "hex" && result !== "base64url";
119
+ }
120
+ function parseKeyFormatFlag(values) {
121
+ const fromFlag = values.get("--key-format");
122
+ if (fromFlag === void 0) return void 0;
123
+ if (fromFlag === "") return "--key-format requires a value";
124
+ if (fromFlag === "hex" || fromFlag === "base64url") return fromFlag;
125
+ return `--key-format must be hex or base64url, got '${fromFlag}'`;
126
+ }
127
+ function parseKeygenFormat(values) {
128
+ const fromFlag = parseKeyFormatFlag(values);
129
+ if (fromFlag === void 0) return "hex";
130
+ return fromFlag;
131
+ }
132
+ function parseOpaqueKeyFormat(values, opts) {
133
+ const fromFlag = parseKeyFormatFlag(values);
134
+ if (fromFlag !== void 0) return fromFlag;
135
+ const fromEnv = (opts.env ?? process.env).IDS_KEY_FORMAT;
136
+ if (fromEnv === void 0 || fromEnv === "") return "hex";
137
+ if (fromEnv === "hex" || fromEnv === "base64url") return fromEnv;
138
+ return `IDS_KEY_FORMAT must be hex or base64url, got '${fromEnv}'`;
139
+ }
140
+ async function loadOpaqueKey(opts, format) {
141
+ const raw = (opts.env ?? process.env).IDS_KEY;
142
+ if (raw === void 0 || raw === "") return "missing IDS_KEY environment variable";
143
+ try {
144
+ return importOpaqueKey(decodeOpaqueKey(raw, format));
145
+ } catch (err) {
146
+ return err.message;
147
+ }
148
+ }
149
+ //#endregion
150
+ //#region src/cli/commands/generate.ts
151
+ function runGenerate(args, opts) {
34
152
  const { flags, values, positionals, errors } = splitFlags(args);
35
- const unsupported = unsupportedFlagForCommand("inspect", flags, new Set(["--opaque", "--key-format"]));
153
+ const unsupported = unsupportedFlagForCommand("generate", flags, new Set([
154
+ "--count",
155
+ "-c",
156
+ "--opaque",
157
+ "--key-format"
158
+ ]));
36
159
  if (unsupported !== void 0) {
37
160
  opts.stderr(unsupported + "\n");
38
161
  return Promise.resolve(1);
@@ -41,57 +164,41 @@ function runInspect(args, opts) {
41
164
  opts.stderr(errors[0] + "\n");
42
165
  return Promise.resolve(1);
43
166
  }
44
- const [input] = positionals;
45
- if (input === void 0) {
46
- opts.stderr(usage());
47
- return Promise.resolve(1);
48
- }
49
167
  const extra = positionals[1];
50
168
  if (extra !== void 0) {
51
169
  opts.stderr(`unexpected argument: ${extra}\n`);
52
170
  return Promise.resolve(1);
53
171
  }
172
+ const [brand] = positionals;
173
+ const count = parseCount(values);
174
+ if (typeof count === "string") {
175
+ opts.stderr(count + "\n");
176
+ return Promise.resolve(1);
177
+ }
54
178
  const opaque = flags.has("--opaque");
55
179
  if (!opaque && flags.has("--key-format")) {
56
180
  opts.stderr("--key-format requires --opaque\n");
57
181
  return Promise.resolve(1);
58
182
  }
59
- const brand = input.slice(0, 3).toLowerCase();
60
183
  if (opaque) {
61
184
  const format = parseOpaqueKeyFormat(values, opts);
62
185
  if (isKeyFormatError(format)) {
63
186
  opts.stderr(format + "\n");
64
187
  return Promise.resolve(1);
65
188
  }
66
- return runOpaqueInspect(brand, input, format, opts);
189
+ return runOpaqueGenerate(brand ?? "", count, format, opts);
67
190
  }
68
191
  let codec;
69
192
  try {
70
- codec = createId(brand, codecOpts(opts));
193
+ codec = createTimestampId(brand ?? "", codecOpts(opts));
71
194
  } catch (err) {
72
195
  opts.stderr(err.message + "\n");
73
196
  return Promise.resolve(1);
74
197
  }
75
- const validation = codec["~standard"].validate(input);
76
- if (validation.issues) {
77
- opts.stderr(validation.issues[0].message + "\n");
78
- return Promise.resolve(1);
79
- }
80
- const canonical = validation.value;
81
- const timestamp = codec.extractTimestamp(canonical);
82
- const nowMs = (opts.now ?? Date.now)();
83
- const relative = formatRelative(timestamp.getTime(), nowMs);
84
- const inputLine = describeInputForm(input, canonical);
85
- opts.stdout([
86
- `brand: ${brand}`,
87
- `timestamp: ${timestamp.toISOString()} (${relative})`,
88
- `canonical: ${canonical}`,
89
- `input: ${inputLine}`,
90
- ""
91
- ].join("\n"));
198
+ for (let i = 0; i < count; i++) opts.stdout(codec.generate() + "\n");
92
199
  return Promise.resolve(0);
93
200
  }
94
- async function runOpaqueInspect(brand, input, format, opts) {
201
+ async function runOpaqueGenerate(brand, count, format, opts) {
95
202
  const keyResult = await loadOpaqueKey(opts, format);
96
203
  if (typeof keyResult === "string") {
97
204
  opts.stderr(keyResult + "\n");
@@ -99,7 +206,7 @@ async function runOpaqueInspect(brand, input, format, opts) {
99
206
  }
100
207
  let codec;
101
208
  try {
102
- codec = createOpaqueId(brand, {
209
+ codec = createOpaqueTimestampId(brand, {
103
210
  key: keyResult,
104
211
  ...codecOpts(opts)
105
212
  });
@@ -107,25 +214,21 @@ async function runOpaqueInspect(brand, input, format, opts) {
107
214
  opts.stderr(err.message + "\n");
108
215
  return 1;
109
216
  }
110
- const validation = codec["~standard"].validate(input);
111
- if (validation.issues) {
112
- opts.stderr(validation.issues[0].message + "\n");
113
- return 1;
114
- }
115
- const canonical = validation.value;
116
- const timestamp = await codec.extractTimestamp(canonical);
117
- const nowMs = (opts.now ?? Date.now)();
118
- const relative = formatRelative(timestamp.getTime(), nowMs);
119
- const inputLine = describeInputForm(input, canonical);
120
- opts.stderr("note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp\n");
121
- opts.stdout([
122
- `brand: ${brand}`,
123
- `timestamp: ${timestamp.toISOString()} (${relative})`,
124
- `canonical: ${canonical}`,
217
+ for (let i = 0; i < count; i++) opts.stdout(await codec.generate() + "\n");
218
+ return 0;
219
+ }
220
+ //#endregion
221
+ //#region src/cli/format.ts
222
+ function formatInspectOutput(result) {
223
+ const relative = formatRelative(result.timestamp.getTime(), result.nowMs);
224
+ const inputLine = describeInputForm(result.input, result.canonical);
225
+ return [
226
+ `brand: ${result.brand}`,
227
+ `timestamp: ${result.timestamp.toISOString()} (${relative})`,
228
+ `canonical: ${result.canonical}`,
125
229
  `input: ${inputLine}`,
126
230
  ""
127
- ].join("\n"));
128
- return 0;
231
+ ].join("\n");
129
232
  }
130
233
  function describeInputForm(input, canonical) {
131
234
  if (input === canonical) return "canonical";
@@ -160,14 +263,29 @@ function headUnits(abs) {
160
263
  function unit(n, name) {
161
264
  return `${n} ${n === 1 ? name : `${name}s`}`;
162
265
  }
163
- function runGenerate(args, opts) {
266
+ //#endregion
267
+ //#region src/cli/usage.ts
268
+ function usage() {
269
+ return [
270
+ "Usage: ids <subcommand> [args]",
271
+ "",
272
+ "Subcommands:",
273
+ " inspect, i <id> [--opaque] [--key-format hex|base64url]",
274
+ " Decode an ID and print brand, timestamp, and canonical form.",
275
+ " --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
276
+ " generate, g <brand> [--count, -c N] [--opaque] [--key-format hex|base64url]",
277
+ ` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,
278
+ " --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
279
+ " keygen, k [--bits 128|192|256] [--key-format hex|base64url]",
280
+ " Emit a random AES key for importOpaqueKey (stdout only).",
281
+ ""
282
+ ].join("\n");
283
+ }
284
+ //#endregion
285
+ //#region src/cli/commands/inspect.ts
286
+ function runInspect(args, opts) {
164
287
  const { flags, values, positionals, errors } = splitFlags(args);
165
- const unsupported = unsupportedFlagForCommand("generate", flags, new Set([
166
- "--count",
167
- "-c",
168
- "--opaque",
169
- "--key-format"
170
- ]));
288
+ const unsupported = unsupportedFlagForCommand("inspect", flags, new Set(["--opaque", "--key-format"]));
171
289
  if (unsupported !== void 0) {
172
290
  opts.stderr(unsupported + "\n");
173
291
  return Promise.resolve(1);
@@ -176,41 +294,55 @@ function runGenerate(args, opts) {
176
294
  opts.stderr(errors[0] + "\n");
177
295
  return Promise.resolve(1);
178
296
  }
297
+ const [input] = positionals;
298
+ if (input === void 0) {
299
+ opts.stderr(usage());
300
+ return Promise.resolve(1);
301
+ }
179
302
  const extra = positionals[1];
180
303
  if (extra !== void 0) {
181
304
  opts.stderr(`unexpected argument: ${extra}\n`);
182
305
  return Promise.resolve(1);
183
306
  }
184
- const [brand] = positionals;
185
- const count = parseCount(values);
186
- if (typeof count === "string") {
187
- opts.stderr(count + "\n");
188
- return Promise.resolve(1);
189
- }
190
307
  const opaque = flags.has("--opaque");
191
308
  if (!opaque && flags.has("--key-format")) {
192
309
  opts.stderr("--key-format requires --opaque\n");
193
310
  return Promise.resolve(1);
194
311
  }
312
+ const brand = input.slice(0, 3).toLowerCase();
195
313
  if (opaque) {
196
314
  const format = parseOpaqueKeyFormat(values, opts);
197
315
  if (isKeyFormatError(format)) {
198
316
  opts.stderr(format + "\n");
199
317
  return Promise.resolve(1);
200
318
  }
201
- return runOpaqueGenerate(brand ?? "", count, format, opts);
319
+ return runOpaqueInspect(brand, input, format, opts);
202
320
  }
203
321
  let codec;
204
322
  try {
205
- codec = createId(brand ?? "", codecOpts(opts));
323
+ codec = createTimestampId(brand, codecOpts(opts));
206
324
  } catch (err) {
207
325
  opts.stderr(err.message + "\n");
208
326
  return Promise.resolve(1);
209
327
  }
210
- for (let i = 0; i < count; i++) opts.stdout(codec.generate() + "\n");
328
+ const validation = codec["~standard"].validate(input);
329
+ if (validation.issues) {
330
+ opts.stderr(validation.issues[0].message + "\n");
331
+ return Promise.resolve(1);
332
+ }
333
+ const canonical = validation.value;
334
+ const timestamp = codec.extractTimestamp(canonical);
335
+ const nowMs = (opts.now ?? Date.now)();
336
+ opts.stdout(formatInspectOutput({
337
+ brand,
338
+ timestamp,
339
+ canonical,
340
+ input,
341
+ nowMs
342
+ }));
211
343
  return Promise.resolve(0);
212
344
  }
213
- async function runOpaqueGenerate(brand, count, format, opts) {
345
+ async function runOpaqueInspect(brand, input, format, opts) {
214
346
  const keyResult = await loadOpaqueKey(opts, format);
215
347
  if (typeof keyResult === "string") {
216
348
  opts.stderr(keyResult + "\n");
@@ -218,7 +350,7 @@ async function runOpaqueGenerate(brand, count, format, opts) {
218
350
  }
219
351
  let codec;
220
352
  try {
221
- codec = createOpaqueId(brand, {
353
+ codec = createOpaqueTimestampId(brand, {
222
354
  key: keyResult,
223
355
  ...codecOpts(opts)
224
356
  });
@@ -226,9 +358,26 @@ async function runOpaqueGenerate(brand, count, format, opts) {
226
358
  opts.stderr(err.message + "\n");
227
359
  return 1;
228
360
  }
229
- for (let i = 0; i < count; i++) opts.stdout(await codec.generate() + "\n");
361
+ const validation = codec["~standard"].validate(input);
362
+ if (validation.issues) {
363
+ opts.stderr(validation.issues[0].message + "\n");
364
+ return 1;
365
+ }
366
+ const canonical = validation.value;
367
+ const timestamp = await codec.extractTimestamp(canonical);
368
+ const nowMs = (opts.now ?? Date.now)();
369
+ opts.stderr("note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp\n");
370
+ opts.stdout(formatInspectOutput({
371
+ brand,
372
+ timestamp,
373
+ canonical,
374
+ input,
375
+ nowMs
376
+ }));
230
377
  return 0;
231
378
  }
379
+ //#endregion
380
+ //#region src/cli/commands/keygen.ts
232
381
  function runKeygen(args, opts) {
233
382
  const { flags, values, positionals, errors } = splitFlags(args);
234
383
  const unsupported = unsupportedFlagForCommand("keygen", flags, new Set(["--bits", "--key-format"]));
@@ -260,140 +409,32 @@ function runKeygen(args, opts) {
260
409
  opts.stdout(encodeOpaqueKey(bytes, format) + "\n");
261
410
  return Promise.resolve(0);
262
411
  }
263
- async function loadOpaqueKey(opts, format) {
264
- const raw = (opts.env ?? process.env).IDS_KEY;
265
- if (raw === void 0 || raw === "") return "missing IDS_KEY environment variable";
266
- try {
267
- return importOpaqueKey(decodeOpaqueKey(raw, format));
268
- } catch (err) {
269
- return err.message;
412
+ //#endregion
413
+ //#region src/cli.ts
414
+ const commands = [
415
+ {
416
+ names: ["generate", "g"],
417
+ run: runGenerate
418
+ },
419
+ {
420
+ names: ["inspect", "i"],
421
+ run: runInspect
422
+ },
423
+ {
424
+ names: ["keygen", "k"],
425
+ run: runKeygen
270
426
  }
271
- }
272
- function parseCount(values) {
273
- const raw = values.get("--count") ?? values.get("-c");
274
- if (raw === void 0) return 1;
275
- if (raw === "") return "--count requires a value";
276
- if (!/^[1-9][0-9]*$/.test(raw)) return `--count must be a positive integer, got '${raw}'`;
277
- return Number(raw);
278
- }
279
- function parseBits(values) {
280
- const raw = values.get("--bits");
281
- if (raw === void 0) return 256;
282
- if (raw === "") return "--bits requires a value";
283
- if (raw === "128") return 128;
284
- if (raw === "192") return 192;
285
- if (raw === "256") return 256;
286
- return `--bits must be 128, 192, or 256, got '${raw}'`;
287
- }
288
- function isKeyFormatError(result) {
289
- return result !== "hex" && result !== "base64url";
290
- }
291
- function parseKeyFormatFlag(values) {
292
- const fromFlag = values.get("--key-format");
293
- if (fromFlag === void 0) return void 0;
294
- if (fromFlag === "") return "--key-format requires a value";
295
- if (fromFlag === "hex" || fromFlag === "base64url") return fromFlag;
296
- return `--key-format must be hex or base64url, got '${fromFlag}'`;
297
- }
298
- function parseKeygenFormat(values) {
299
- const fromFlag = parseKeyFormatFlag(values);
300
- if (fromFlag === void 0) return "hex";
301
- return fromFlag;
302
- }
303
- function parseOpaqueKeyFormat(values, opts) {
304
- const fromFlag = parseKeyFormatFlag(values);
305
- if (fromFlag !== void 0) return fromFlag;
306
- const fromEnv = (opts.env ?? process.env).IDS_KEY_FORMAT;
307
- if (fromEnv === void 0 || fromEnv === "") return "hex";
308
- if (fromEnv === "hex" || fromEnv === "base64url") return fromEnv;
309
- return `IDS_KEY_FORMAT must be hex or base64url, got '${fromEnv}'`;
310
- }
311
- function splitFlagToken(arg) {
312
- const eq = arg.indexOf("=");
313
- if (eq <= 0) return {
314
- flag: arg,
315
- inlineValue: void 0
316
- };
317
- return {
318
- flag: arg.slice(0, eq),
319
- inlineValue: arg.slice(eq + 1)
320
- };
321
- }
322
- function splitFlags(args) {
323
- const flags = /* @__PURE__ */ new Set();
324
- const values = /* @__PURE__ */ new Map();
325
- const positionals = [];
326
- const errors = [];
327
- const seenFlags = /* @__PURE__ */ new Set();
328
- const valueFlags = new Set([
329
- "--count",
330
- "-c",
331
- "--bits",
332
- "--key-format"
333
- ]);
334
- const addFlag = (flag) => {
335
- const canonical = canonicalFlag(flag);
336
- if (seenFlags.has(canonical)) errors.push(`duplicate flag: ${canonical}`);
337
- seenFlags.add(canonical);
338
- flags.add(flag);
339
- };
340
- for (let i = 0; i < args.length; i++) {
341
- const raw = args[i];
342
- const { flag, inlineValue } = splitFlagToken(raw);
343
- if (flag === "--opaque") {
344
- addFlag(flag);
345
- if (inlineValue !== void 0) errors.push("flag does not take a value: --opaque");
346
- continue;
347
- }
348
- if (valueFlags.has(flag)) {
349
- if (inlineValue !== void 0) {
350
- addFlag(flag);
351
- values.set(flag, inlineValue);
352
- continue;
353
- }
354
- const value = args[i + 1];
355
- if (value === void 0 || value.startsWith("-")) {
356
- addFlag(flag);
357
- values.set(flag, "");
358
- continue;
359
- }
360
- addFlag(flag);
361
- values.set(flag, value);
362
- i++;
363
- continue;
364
- }
365
- if (flag.startsWith("-")) {
366
- addFlag(flag);
367
- continue;
368
- }
369
- positionals.push(raw);
427
+ ];
428
+ async function run(opts) {
429
+ const [subcommand, ...rest] = opts.argv;
430
+ const command = commands.find((candidate) => candidate.names.includes(subcommand ?? ""));
431
+ if (command !== void 0) return command.run(rest, opts);
432
+ if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h") {
433
+ opts.stdout(usage());
434
+ return 0;
370
435
  }
371
- return {
372
- flags,
373
- values,
374
- positionals,
375
- errors
376
- };
377
- }
378
- function canonicalFlag(flag) {
379
- if (flag === "-c") return "--count";
380
- return flag;
381
- }
382
- const knownFlags = new Set([
383
- "--opaque",
384
- "--key-format",
385
- "--count",
386
- "-c",
387
- "--bits"
388
- ]);
389
- function unsupportedFlagForCommand(command, flags, allowed) {
390
- for (const flag of flags) if (!allowed.has(flag)) return knownFlags.has(flag) ? `unsupported flag for ${command}: ${flag}` : `unsupported flag: ${flag}`;
391
- }
392
- function codecOpts(opts) {
393
- const o = { allowDuplicateBrand: true };
394
- if (opts.now !== void 0) o.now = opts.now;
395
- if (opts.rng !== void 0) o.rng = opts.rng;
396
- return o;
436
+ opts.stderr(usage());
437
+ return 1;
397
438
  }
398
439
  //#endregion
399
440
  //#region bin/cli.ts