@smonn/ids 0.12.3 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -32
- package/dist/{adapter-types-CdYJM6Sf.d.mts → adapter-types-CIc-4O-P.d.mts} +2 -2
- package/dist/{adapter-types-CdYJM6Sf.d.mts.map → adapter-types-CIc-4O-P.d.mts.map} +1 -1
- package/dist/cli.mjs +316 -178
- package/dist/cli.mjs.map +1 -1
- package/dist/{codec-shell-DvrTDa65.mjs → codec-shell-C2NKQEx2.mjs} +5 -1
- package/dist/codec-shell-C2NKQEx2.mjs.map +1 -0
- package/dist/{digest-CknNw2wa.mjs → digest-DsGeXfk3.mjs} +9 -24
- package/dist/digest-DsGeXfk3.mjs.map +1 -0
- package/dist/digest.d.mts +3 -3
- package/dist/digest.d.mts.map +1 -1
- package/dist/digest.mjs +1 -1
- package/dist/drizzle.d.mts +3 -3
- package/dist/drizzle.d.mts.map +1 -1
- package/dist/drizzle.mjs.map +1 -1
- package/dist/error-Cp5qYZcv.mjs.map +1 -1
- package/dist/{error-JIPylU_E.d.mts → error-Dqyho9vp.d.mts} +7 -2
- package/dist/error-Dqyho9vp.d.mts.map +1 -0
- package/dist/express.d.mts +2 -2
- package/dist/fastify.d.mts +2 -2
- package/dist/graphql.d.mts +3 -3
- package/dist/graphql.mjs +2 -2
- package/dist/graphql.mjs.map +1 -1
- package/dist/hono.d.mts +2 -2
- package/dist/hono.mjs +1 -2
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +21 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{key-material-f29JIyrz.mjs → key-material-DvjACe89.mjs} +53 -2
- package/dist/key-material-DvjACe89.mjs.map +1 -0
- package/dist/kysely.d.mts +3 -3
- package/dist/kysely.d.mts.map +1 -1
- package/dist/kysely.mjs.map +1 -1
- package/dist/mikro-orm.d.mts +3 -3
- package/dist/mikro-orm.d.mts.map +1 -1
- package/dist/mikro-orm.mjs.map +1 -1
- package/dist/nestjs.d.mts +2 -2
- package/dist/nestjs.mjs +1 -1
- package/dist/nestjs.mjs.map +1 -1
- package/dist/{opaque-BQVNoIIh.mjs → opaque-BW3Uzeeb.mjs} +5 -28
- package/dist/opaque-BW3Uzeeb.mjs.map +1 -0
- package/dist/opaque.d.mts +22 -4
- package/dist/opaque.d.mts.map +1 -1
- package/dist/opaque.mjs +1 -1
- package/dist/prisma.d.mts +32 -27
- package/dist/prisma.d.mts.map +1 -1
- package/dist/prisma.mjs +11 -15
- package/dist/prisma.mjs.map +1 -1
- package/dist/{reverse-DsPd7Lco.mjs → reverse-BW8g_cln.mjs} +12 -5
- package/dist/reverse-BW8g_cln.mjs.map +1 -0
- package/dist/reverse.d.mts +20 -4
- package/dist/reverse.d.mts.map +1 -1
- package/dist/reverse.mjs +1 -1
- package/dist/{rng-Clos6uC0.mjs → rng-BHFxX1Fc.mjs} +2 -2
- package/dist/{rng-Clos6uC0.mjs.map → rng-BHFxX1Fc.mjs.map} +1 -1
- package/dist/{signed-4h2BnlWx.mjs → signed-BTz3ZFYE.mjs} +12 -33
- package/dist/signed-BTz3ZFYE.mjs.map +1 -0
- package/dist/signed.d.mts +13 -4
- package/dist/signed.d.mts.map +1 -1
- package/dist/signed.mjs +1 -1
- package/dist/{timestamp-Cg9nRfnK.mjs → timestamp-CleAIdZI.mjs} +12 -5
- package/dist/timestamp-CleAIdZI.mjs.map +1 -0
- package/dist/typeorm.d.mts +2 -2
- package/dist/typeorm.d.mts.map +1 -1
- package/dist/typeorm.mjs.map +1 -1
- package/dist/{types-g7CiQDyE.d.mts → types-wplmOgOK.d.mts} +20 -3
- package/dist/types-wplmOgOK.d.mts.map +1 -0
- package/dist/{wrapped-BQ-lNECo.mjs → wrapped-DPlsv1x-.mjs} +18 -76
- package/dist/wrapped-DPlsv1x-.mjs.map +1 -0
- package/dist/wrapped.d.mts +31 -5
- package/dist/wrapped.d.mts.map +1 -1
- package/dist/wrapped.mjs +1 -1
- package/package.json +80 -27
- package/dist/codec-shell-DvrTDa65.mjs.map +0 -1
- package/dist/digest-CknNw2wa.mjs.map +0 -1
- package/dist/error-JIPylU_E.d.mts.map +0 -1
- package/dist/key-material-f29JIyrz.mjs.map +0 -1
- package/dist/opaque-BQVNoIIh.mjs.map +0 -1
- package/dist/reverse-DsPd7Lco.mjs.map +0 -1
- package/dist/signed-4h2BnlWx.mjs.map +0 -1
- package/dist/timestamp-Cg9nRfnK.mjs.map +0 -1
- package/dist/types-g7CiQDyE.d.mts.map +0 -1
- package/dist/wrapped-BQ-lNECo.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,12 +1,85 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as isIdsError } from "./error-Cp5qYZcv.mjs";
|
|
3
|
-
import { t as createTimestampId } from "./timestamp-
|
|
4
|
-
import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-
|
|
5
|
-
import { t as createReverseTimestampId } from "./reverse-
|
|
6
|
-
import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-
|
|
7
|
-
import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-
|
|
8
|
-
import { i as importDigestKey, n as decodeDigestKey, r as encodeDigestKey, t as createDigestId } from "./digest-
|
|
3
|
+
import { t as createTimestampId } from "./timestamp-CleAIdZI.mjs";
|
|
4
|
+
import { i as importOpaqueKey, n as decodeOpaqueKey, r as encodeOpaqueKey, t as createOpaqueTimestampId } from "./opaque-BW3Uzeeb.mjs";
|
|
5
|
+
import { t as createReverseTimestampId } from "./reverse-BW8g_cln.mjs";
|
|
6
|
+
import { i as importSigningKey, n as decodeSigningKey, r as encodeSigningKey, t as createSignedTimestampId } from "./signed-BTz3ZFYE.mjs";
|
|
7
|
+
import { i as importWrappingKey, n as decodeWrappingKey, r as encodeWrappingKey, t as createWrappedKeyId } from "./wrapped-DPlsv1x-.mjs";
|
|
8
|
+
import { i as importDigestKey, n as decodeDigestKey, r as encodeDigestKey, t as createDigestId } from "./digest-DsGeXfk3.mjs";
|
|
9
|
+
//#region src/cli/format.ts
|
|
10
|
+
const invalidIdPrefix = "invalid_id: ";
|
|
11
|
+
function formatCliError(err) {
|
|
12
|
+
return isIdsError(err) ? `${err.code}: ${err.message}` : err instanceof Error ? err.message : String(err);
|
|
13
|
+
}
|
|
14
|
+
function formatWrappedInspectOutput(result) {
|
|
15
|
+
const inputLine = describeInputForm(result.input, result.canonical);
|
|
16
|
+
return [
|
|
17
|
+
`brand: ${result.brand}`,
|
|
18
|
+
`lookup-key: ${result.lookupKey.toString()}`,
|
|
19
|
+
`canonical: ${result.canonical}`,
|
|
20
|
+
`input: ${inputLine}`,
|
|
21
|
+
""
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
function formatSignedInspectOutput(result) {
|
|
25
|
+
const relative = formatRelative(result.timestamp.getTime(), result.nowMs);
|
|
26
|
+
const inputLine = describeInputForm(result.input, result.canonical);
|
|
27
|
+
const lines = [`brand: ${result.brand}`, `timestamp: ${result.timestamp.toISOString()} (${relative})`];
|
|
28
|
+
lines.push(`verification: ${result.verification}`);
|
|
29
|
+
lines.push(`canonical: ${result.canonical}`, `input: ${inputLine}`, "");
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
32
|
+
function formatInspectOutput(result) {
|
|
33
|
+
const relative = formatRelative(result.timestamp.getTime(), result.nowMs);
|
|
34
|
+
const inputLine = describeInputForm(result.input, result.canonical);
|
|
35
|
+
return [
|
|
36
|
+
`brand: ${result.brand}`,
|
|
37
|
+
`timestamp: ${result.timestamp.toISOString()} (${relative})`,
|
|
38
|
+
`canonical: ${result.canonical}`,
|
|
39
|
+
`input: ${inputLine}`,
|
|
40
|
+
""
|
|
41
|
+
].join("\n");
|
|
42
|
+
}
|
|
43
|
+
function describeInputForm(input, canonical) {
|
|
44
|
+
if (input === canonical) return "canonical";
|
|
45
|
+
const notes = [];
|
|
46
|
+
if (input !== input.toLowerCase()) notes.push("was uppercase");
|
|
47
|
+
if (/[ilo]/i.test(input.slice(4))) notes.push("used Crockford aliases");
|
|
48
|
+
return `not canonical (${notes.join(" + ")})`;
|
|
49
|
+
}
|
|
50
|
+
const msPerMinute = 60 * 1e3;
|
|
51
|
+
const msPerHour = 60 * msPerMinute;
|
|
52
|
+
const msPerDay = 24 * msPerHour;
|
|
53
|
+
const daysPerMonth = 30.44;
|
|
54
|
+
const monthsPerYear = 12;
|
|
55
|
+
function formatRelative(thenMs, nowMs) {
|
|
56
|
+
const diff = nowMs - thenMs;
|
|
57
|
+
const abs = Math.abs(diff);
|
|
58
|
+
const suffix = diff < 0 ? "from now" : "ago";
|
|
59
|
+
const head = headUnits(abs);
|
|
60
|
+
return head === "" ? "just now" : `${head} ${suffix}`;
|
|
61
|
+
}
|
|
62
|
+
function headUnits(abs) {
|
|
63
|
+
if (abs < 6e4) return "";
|
|
64
|
+
if (abs < 36e5) return unit(Math.round(abs / msPerMinute), "minute");
|
|
65
|
+
if (abs < 864e5) return unit(Math.round(abs / msPerHour), "hour");
|
|
66
|
+
if (abs < 864e5 * daysPerMonth) return unit(Math.round(abs / msPerDay), "day");
|
|
67
|
+
const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));
|
|
68
|
+
if (totalMonths < monthsPerYear) return unit(totalMonths, "month");
|
|
69
|
+
const years = Math.floor(totalMonths / monthsPerYear);
|
|
70
|
+
const months = totalMonths % monthsPerYear;
|
|
71
|
+
return months === 0 ? unit(years, "year") : `${unit(years, "year")} ${unit(months, "month")}`;
|
|
72
|
+
}
|
|
73
|
+
function unit(n, name) {
|
|
74
|
+
return `${n} ${n === 1 ? name : `${name}s`}`;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
9
77
|
//#region src/cli/key-io.ts
|
|
78
|
+
function isLoadKeyError(value) {
|
|
79
|
+
if (typeof value !== "object" || value === null) return false;
|
|
80
|
+
const kind = value.kind;
|
|
81
|
+
return kind === "missing" || kind === "import-failure";
|
|
82
|
+
}
|
|
10
83
|
function isKeyFormatError(result) {
|
|
11
84
|
return result !== "hex" && result !== "base64url";
|
|
12
85
|
}
|
|
@@ -32,11 +105,17 @@ function parseKeyFormat(values, opts, facet) {
|
|
|
32
105
|
}
|
|
33
106
|
async function loadKey(opts, format, facet) {
|
|
34
107
|
const raw = (opts.env ?? process.env)[facet.envVar];
|
|
35
|
-
if (raw === void 0 || raw === "") return
|
|
108
|
+
if (raw === void 0 || raw === "") return {
|
|
109
|
+
kind: "missing",
|
|
110
|
+
message: `missing ${facet.envVar} environment variable`
|
|
111
|
+
};
|
|
36
112
|
try {
|
|
37
113
|
return await facet.import(facet.decode(raw, format));
|
|
38
114
|
} catch (err) {
|
|
39
|
-
return
|
|
115
|
+
return {
|
|
116
|
+
kind: "import-failure",
|
|
117
|
+
message: formatCliError(err)
|
|
118
|
+
};
|
|
40
119
|
}
|
|
41
120
|
}
|
|
42
121
|
//#endregion
|
|
@@ -167,76 +246,21 @@ function isNsError(result) {
|
|
|
167
246
|
return result === "--ns requires a value";
|
|
168
247
|
}
|
|
169
248
|
//#endregion
|
|
170
|
-
//#region src/cli/format.ts
|
|
171
|
-
function formatCliError(err) {
|
|
172
|
-
return isIdsError(err) ? `${err.code}: ${err.message}` : err instanceof Error ? err.message : String(err);
|
|
173
|
-
}
|
|
174
|
-
function formatWrappedInspectOutput(result) {
|
|
175
|
-
const inputLine = describeInputForm(result.input, result.canonical);
|
|
176
|
-
return [
|
|
177
|
-
`brand: ${result.brand}`,
|
|
178
|
-
`lookup-key: ${result.lookupKey.toString()}`,
|
|
179
|
-
`canonical: ${result.canonical}`,
|
|
180
|
-
`input: ${inputLine}`,
|
|
181
|
-
""
|
|
182
|
-
].join("\n");
|
|
183
|
-
}
|
|
184
|
-
function formatSignedInspectOutput(result) {
|
|
185
|
-
const relative = formatRelative(result.timestamp.getTime(), result.nowMs);
|
|
186
|
-
const inputLine = describeInputForm(result.input, result.canonical);
|
|
187
|
-
const lines = [`brand: ${result.brand}`, `timestamp: ${result.timestamp.toISOString()} (${relative})`];
|
|
188
|
-
lines.push(`verification: ${result.verification}`);
|
|
189
|
-
lines.push(`canonical: ${result.canonical}`, `input: ${inputLine}`, "");
|
|
190
|
-
return lines.join("\n");
|
|
191
|
-
}
|
|
192
|
-
function formatInspectOutput(result) {
|
|
193
|
-
const relative = formatRelative(result.timestamp.getTime(), result.nowMs);
|
|
194
|
-
const inputLine = describeInputForm(result.input, result.canonical);
|
|
195
|
-
return [
|
|
196
|
-
`brand: ${result.brand}`,
|
|
197
|
-
`timestamp: ${result.timestamp.toISOString()} (${relative})`,
|
|
198
|
-
`canonical: ${result.canonical}`,
|
|
199
|
-
`input: ${inputLine}`,
|
|
200
|
-
""
|
|
201
|
-
].join("\n");
|
|
202
|
-
}
|
|
203
|
-
function describeInputForm(input, canonical) {
|
|
204
|
-
if (input === canonical) return "canonical";
|
|
205
|
-
const notes = [];
|
|
206
|
-
if (input !== input.toLowerCase()) notes.push("was uppercase");
|
|
207
|
-
if (/[ilo]/i.test(input.slice(4))) notes.push("used Crockford aliases");
|
|
208
|
-
return `not canonical (${notes.join(" + ")})`;
|
|
209
|
-
}
|
|
210
|
-
const msPerMinute = 60 * 1e3;
|
|
211
|
-
const msPerHour = 60 * msPerMinute;
|
|
212
|
-
const msPerDay = 24 * msPerHour;
|
|
213
|
-
const daysPerMonth = 30.44;
|
|
214
|
-
const monthsPerYear = 12;
|
|
215
|
-
function formatRelative(thenMs, nowMs) {
|
|
216
|
-
const diff = nowMs - thenMs;
|
|
217
|
-
const abs = Math.abs(diff);
|
|
218
|
-
const suffix = diff < 0 ? "from now" : "ago";
|
|
219
|
-
const head = headUnits(abs);
|
|
220
|
-
return head === "" ? "just now" : `${head} ${suffix}`;
|
|
221
|
-
}
|
|
222
|
-
function headUnits(abs) {
|
|
223
|
-
if (abs < 6e4) return "";
|
|
224
|
-
if (abs < 36e5) return unit(Math.round(abs / msPerMinute), "minute");
|
|
225
|
-
if (abs < 864e5) return unit(Math.round(abs / msPerHour), "hour");
|
|
226
|
-
if (abs < 864e5 * daysPerMonth) return unit(Math.round(abs / msPerDay), "day");
|
|
227
|
-
const totalMonths = Math.round(abs / (msPerDay * daysPerMonth));
|
|
228
|
-
if (totalMonths < monthsPerYear) return unit(totalMonths, "month");
|
|
229
|
-
const years = Math.floor(totalMonths / monthsPerYear);
|
|
230
|
-
const months = totalMonths % monthsPerYear;
|
|
231
|
-
return months === 0 ? unit(years, "year") : `${unit(years, "year")} ${unit(months, "month")}`;
|
|
232
|
-
}
|
|
233
|
-
function unit(n, name) {
|
|
234
|
-
return `${n} ${n === 1 ? name : `${name}s`}`;
|
|
235
|
-
}
|
|
236
|
-
//#endregion
|
|
237
249
|
//#region src/cli/variants.ts
|
|
250
|
+
function standardValidate(codec, input) {
|
|
251
|
+
const result = codec["~standard"].validate(input);
|
|
252
|
+
if (result.issues) return { issue: invalidIdPrefix + result.issues[0].message };
|
|
253
|
+
return { value: result.value };
|
|
254
|
+
}
|
|
238
255
|
const timestampVariant = {
|
|
239
|
-
|
|
256
|
+
inspect: {
|
|
257
|
+
mode: "readable",
|
|
258
|
+
note: "note: timestamp assumes a plaintext Timestamp ID; if this ID was Opaque-encoded, the timestamp is meaningless — re-run with --opaque and the correct IDS_KEY",
|
|
259
|
+
validate: standardValidate,
|
|
260
|
+
extractTimestamp(codec, id) {
|
|
261
|
+
return codec.extractTimestamp(id);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
240
264
|
construct(brand, opts) {
|
|
241
265
|
try {
|
|
242
266
|
return createTimestampId(brand, codecOpts(opts));
|
|
@@ -254,7 +278,14 @@ const opaqueVariant = {
|
|
|
254
278
|
decode: decodeOpaqueKey,
|
|
255
279
|
import: importOpaqueKey
|
|
256
280
|
},
|
|
257
|
-
|
|
281
|
+
inspect: {
|
|
282
|
+
mode: "keyed-readable",
|
|
283
|
+
note: "note: timestamp assumes IDS_KEY matches the key used at generation; a wrong key yields a plausible but incorrect timestamp",
|
|
284
|
+
validate: standardValidate,
|
|
285
|
+
extractTimestamp(codec, id) {
|
|
286
|
+
return codec.extractTimestamp(id);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
258
289
|
construct(brand, opts, key) {
|
|
259
290
|
try {
|
|
260
291
|
return createOpaqueTimestampId(brand, {
|
|
@@ -268,7 +299,14 @@ const opaqueVariant = {
|
|
|
268
299
|
};
|
|
269
300
|
const reverseVariant = {
|
|
270
301
|
flag: "--reverse",
|
|
271
|
-
|
|
302
|
+
inspect: {
|
|
303
|
+
mode: "readable",
|
|
304
|
+
note: "note: timestamp assumes a plaintext Timestamp ID; if this ID was Opaque-encoded, the timestamp is meaningless — re-run with --opaque and the correct IDS_KEY",
|
|
305
|
+
validate: standardValidate,
|
|
306
|
+
extractTimestamp(codec, id) {
|
|
307
|
+
return codec.extractTimestamp(id);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
272
310
|
construct(brand, opts) {
|
|
273
311
|
try {
|
|
274
312
|
return createReverseTimestampId(brand, codecOpts(opts));
|
|
@@ -286,7 +324,13 @@ const wrappedVariant = {
|
|
|
286
324
|
decode: decodeWrappingKey,
|
|
287
325
|
import: importWrappingKey
|
|
288
326
|
},
|
|
289
|
-
|
|
327
|
+
inspect: {
|
|
328
|
+
mode: "unwrap",
|
|
329
|
+
validate: standardValidate,
|
|
330
|
+
unwrap(codec, id) {
|
|
331
|
+
return codec.unwrap(id);
|
|
332
|
+
}
|
|
333
|
+
},
|
|
290
334
|
extraFlags: ["--kind"],
|
|
291
335
|
construct(brand, _opts, key, values) {
|
|
292
336
|
const kind = parseKind(values ?? /* @__PURE__ */ new Map());
|
|
@@ -312,7 +356,12 @@ const signedVariant = {
|
|
|
312
356
|
decode: decodeSigningKey,
|
|
313
357
|
import: importSigningKey
|
|
314
358
|
},
|
|
315
|
-
|
|
359
|
+
inspect: {
|
|
360
|
+
mode: "verify",
|
|
361
|
+
safeVerify(codec, id) {
|
|
362
|
+
return codec.safeVerify(id);
|
|
363
|
+
}
|
|
364
|
+
},
|
|
316
365
|
construct(brand, opts, key) {
|
|
317
366
|
try {
|
|
318
367
|
return createSignedTimestampId(brand, {
|
|
@@ -333,7 +382,7 @@ const digestVariant = {
|
|
|
333
382
|
decode: decodeDigestKey,
|
|
334
383
|
import: importDigestKey
|
|
335
384
|
},
|
|
336
|
-
|
|
385
|
+
inspect: { mode: "unsupported" },
|
|
337
386
|
extraFlags: ["--ns"],
|
|
338
387
|
construct(brand, opts, key, values) {
|
|
339
388
|
const ns = parseNs(values ?? /* @__PURE__ */ new Map());
|
|
@@ -347,8 +396,9 @@ const digestVariant = {
|
|
|
347
396
|
});
|
|
348
397
|
return {
|
|
349
398
|
safeParse: (v) => codec.safeParse(v),
|
|
350
|
-
generate() {
|
|
351
|
-
|
|
399
|
+
async generate() {
|
|
400
|
+
const material = await (opts.readStdin ?? (() => Promise.resolve("")))();
|
|
401
|
+
return codec.digest(material);
|
|
352
402
|
}
|
|
353
403
|
};
|
|
354
404
|
} catch (err) {
|
|
@@ -394,6 +444,11 @@ const keygenPolicy = {
|
|
|
394
444
|
};
|
|
395
445
|
//#endregion
|
|
396
446
|
//#region src/cli/dispatch.ts
|
|
447
|
+
function isCodecError(v) {
|
|
448
|
+
if (typeof v !== "object" || v === null) return false;
|
|
449
|
+
const kind = v.kind;
|
|
450
|
+
return (kind === "usage" || kind === "runtime") && "message" in v;
|
|
451
|
+
}
|
|
397
452
|
function deriveAllowedFlags(policy) {
|
|
398
453
|
const flags = new Set(policy.intrinsicFlags);
|
|
399
454
|
let hasKeyed = policy.default.key !== void 0;
|
|
@@ -415,12 +470,106 @@ async function buildCodec(variant, brand, values, opts) {
|
|
|
415
470
|
let key;
|
|
416
471
|
if (variant.key !== void 0) {
|
|
417
472
|
const format = parseKeyFormat(values, opts, variant.key);
|
|
418
|
-
if (isKeyFormatError(format)) return
|
|
473
|
+
if (isKeyFormatError(format)) return {
|
|
474
|
+
kind: "usage",
|
|
475
|
+
message: format
|
|
476
|
+
};
|
|
419
477
|
const keyResult = await loadKey(opts, format, variant.key);
|
|
420
|
-
if (
|
|
478
|
+
if (isLoadKeyError(keyResult)) return {
|
|
479
|
+
kind: keyResult.kind === "missing" ? "usage" : "runtime",
|
|
480
|
+
message: keyResult.message
|
|
481
|
+
};
|
|
421
482
|
key = keyResult;
|
|
422
483
|
}
|
|
423
|
-
|
|
484
|
+
const codecOrError = variant.construct(brand, opts, key, values);
|
|
485
|
+
if (typeof codecOrError === "string") return {
|
|
486
|
+
kind: codecOrError.startsWith("--") ? "usage" : "runtime",
|
|
487
|
+
message: codecOrError
|
|
488
|
+
};
|
|
489
|
+
return codecOrError;
|
|
490
|
+
}
|
|
491
|
+
//#endregion
|
|
492
|
+
//#region src/cli/usage.ts
|
|
493
|
+
function usageInspect() {
|
|
494
|
+
return [
|
|
495
|
+
"Usage: ids inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--signed] [--key-format hex|base64url]",
|
|
496
|
+
"",
|
|
497
|
+
" Decode an ID and print brand, timestamp (or lookup key), and canonical form.",
|
|
498
|
+
" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
|
|
499
|
+
" --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).",
|
|
500
|
+
" --kind is required with --wrapped: u32, i32, u64, or i64.",
|
|
501
|
+
" --reverse decodes a Reverse Timestamp ID (newest-first sort order).",
|
|
502
|
+
" --signed decodes a Signed Timestamp ID; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
503
|
+
" Without IDS_SIGNING_KEY, --signed prints the timestamp only (no verification). With IDS_SIGNING_KEY, prints verification: ok or failed.",
|
|
504
|
+
" Note: --digest is not supported for inspect (Digest IDs are one-way; there is no reverse path).",
|
|
505
|
+
""
|
|
506
|
+
].join("\n");
|
|
507
|
+
}
|
|
508
|
+
function usageGenerate() {
|
|
509
|
+
return [
|
|
510
|
+
`Usage: ids generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--signed] [--digest --ns <ns>] [--key-format hex|base64url]`,
|
|
511
|
+
"",
|
|
512
|
+
` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,
|
|
513
|
+
" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
|
|
514
|
+
" --reverse mints Reverse Timestamp IDs (newest-first sort order).",
|
|
515
|
+
" --signed mints Signed Timestamp IDs; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
516
|
+
" --digest mints a deterministic Digest ID from material read on stdin.",
|
|
517
|
+
" --ns <ns> is required: the namespace domain separator (non-secret, non-empty).",
|
|
518
|
+
" Reads the digest key from IDS_DIGEST_KEY (hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).",
|
|
519
|
+
" Same material + ns + key always produces the same ID. Digest IDs are one-way.",
|
|
520
|
+
" --count N > 1 is rejected: same material always produces the same ID.",
|
|
521
|
+
""
|
|
522
|
+
].join("\n");
|
|
523
|
+
}
|
|
524
|
+
function usageKeygen() {
|
|
525
|
+
return [
|
|
526
|
+
"Usage: ids keygen, k [--wrapped] [--signed] [--digest] [--bits 128|192|256] [--key-format hex|base64url]",
|
|
527
|
+
"",
|
|
528
|
+
" Emit a random key for importOpaqueKey, importWrappingKey, importSigningKey, or importDigestKey (stdout only).",
|
|
529
|
+
" --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).",
|
|
530
|
+
" --signed emits a signing key for importSigningKey instead (IDS_SIGNING_KEY; hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
531
|
+
" --digest emits a digest key for importDigestKey instead (IDS_DIGEST_KEY; hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).",
|
|
532
|
+
""
|
|
533
|
+
].join("\n");
|
|
534
|
+
}
|
|
535
|
+
function usage() {
|
|
536
|
+
return [
|
|
537
|
+
"Usage: ids <subcommand> [args]",
|
|
538
|
+
"",
|
|
539
|
+
"Subcommands:",
|
|
540
|
+
" inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--signed] [--key-format hex|base64url]",
|
|
541
|
+
" Decode an ID and print brand, timestamp (or lookup key), and canonical form.",
|
|
542
|
+
" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
|
|
543
|
+
" --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).",
|
|
544
|
+
" --kind is required with --wrapped: u32, i32, u64, or i64.",
|
|
545
|
+
" --reverse decodes a Reverse Timestamp ID (newest-first sort order).",
|
|
546
|
+
" --signed decodes a Signed Timestamp ID; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
547
|
+
" Without IDS_SIGNING_KEY, --signed prints the timestamp only (no verification). With IDS_SIGNING_KEY, prints verification: ok or failed.",
|
|
548
|
+
" Note: --digest is not supported for inspect (Digest IDs are one-way; there is no reverse path).",
|
|
549
|
+
" generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--signed] [--digest --ns <ns>] [--key-format hex|base64url]",
|
|
550
|
+
` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,
|
|
551
|
+
" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
|
|
552
|
+
" --reverse mints Reverse Timestamp IDs (newest-first sort order).",
|
|
553
|
+
" --signed mints Signed Timestamp IDs; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
554
|
+
" --digest mints a deterministic Digest ID from material read on stdin.",
|
|
555
|
+
" --ns <ns> is required: the namespace domain separator (non-secret, non-empty).",
|
|
556
|
+
" Reads the digest key from IDS_DIGEST_KEY (hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).",
|
|
557
|
+
" Same material + ns + key always produces the same ID. Digest IDs are one-way.",
|
|
558
|
+
" --count N > 1 is rejected: same material always produces the same ID.",
|
|
559
|
+
" keygen, k [--wrapped] [--signed] [--digest] [--bits 128|192|256] [--key-format hex|base64url]",
|
|
560
|
+
" Emit a random key for importOpaqueKey, importWrappingKey, importSigningKey, or importDigestKey (key on stdout; warning on stderr).",
|
|
561
|
+
" Safe handling: redirect stdout to a 0600 file (e.g. ids keygen > key.hex && chmod 0600 key.hex);",
|
|
562
|
+
" do not let the key appear in shell history or CI logs. A warning is printed to stderr on every run.",
|
|
563
|
+
" --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).",
|
|
564
|
+
" --signed emits a signing key for importSigningKey instead (IDS_SIGNING_KEY; hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
565
|
+
" --digest emits a digest key for importDigestKey instead (IDS_DIGEST_KEY; hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).",
|
|
566
|
+
"",
|
|
567
|
+
"Exit codes:",
|
|
568
|
+
" 0 Success",
|
|
569
|
+
" 1 Runtime/operational error (codec failure, bad key material, verification failure)",
|
|
570
|
+
" 2 Usage/argument error (unknown subcommand, unrecognised flag, bad flag value, missing required arg)",
|
|
571
|
+
""
|
|
572
|
+
].join("\n");
|
|
424
573
|
}
|
|
425
574
|
//#endregion
|
|
426
575
|
//#region src/cli/commands/generate.ts
|
|
@@ -437,131 +586,106 @@ function readProcessStdin() {
|
|
|
437
586
|
return stdinCache;
|
|
438
587
|
}
|
|
439
588
|
async function runGenerate(args, opts) {
|
|
589
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
590
|
+
opts.stdout(usageGenerate());
|
|
591
|
+
return 0;
|
|
592
|
+
}
|
|
440
593
|
const allowedFlags = deriveAllowedFlags(generatePolicy);
|
|
441
594
|
const selectorFlags = new Set(generatePolicy.selectable.map((v) => v.flag).filter((f) => f !== void 0));
|
|
442
595
|
const { flags, values, positionals, errors } = splitFlags(args, new Set([...allowedFlags].filter((f) => !selectorFlags.has(f))));
|
|
443
596
|
const unsupported = unsupportedFlagForCommand("generate", flags, allowedFlags);
|
|
444
597
|
if (unsupported !== void 0) {
|
|
445
598
|
opts.stderr(unsupported + "\n");
|
|
446
|
-
return
|
|
599
|
+
return 2;
|
|
447
600
|
}
|
|
448
601
|
if (errors[0] !== void 0) {
|
|
449
602
|
opts.stderr(errors[0] + "\n");
|
|
450
|
-
return
|
|
603
|
+
return 2;
|
|
451
604
|
}
|
|
452
605
|
const extra = positionals[1];
|
|
453
606
|
if (extra !== void 0) {
|
|
454
607
|
opts.stderr(`unexpected argument: ${extra}\n`);
|
|
455
|
-
return
|
|
608
|
+
return 2;
|
|
456
609
|
}
|
|
457
610
|
const [brand] = positionals;
|
|
458
611
|
const count = parseCount(values);
|
|
459
612
|
if (typeof count === "string") {
|
|
460
613
|
opts.stderr(count + "\n");
|
|
461
|
-
return
|
|
614
|
+
return 2;
|
|
462
615
|
}
|
|
463
616
|
const variant = resolveVariant(generatePolicy, flags);
|
|
464
617
|
if (typeof variant === "string") {
|
|
465
618
|
opts.stderr(variant + "\n");
|
|
466
|
-
return
|
|
619
|
+
return 2;
|
|
467
620
|
}
|
|
468
621
|
if (variant.key === void 0 && flags.has("--key-format")) {
|
|
469
622
|
opts.stderr("--key-format requires --opaque, --signed, or --digest\n");
|
|
470
|
-
return
|
|
623
|
+
return 2;
|
|
471
624
|
}
|
|
472
625
|
if (flags.has("--digest") && count > 1) {
|
|
473
626
|
opts.stderr("--count N > 1 is rejected with --digest: same material always produces the same ID\n");
|
|
474
|
-
return
|
|
627
|
+
return 2;
|
|
475
628
|
}
|
|
476
629
|
const optsWithStdin = {
|
|
477
630
|
...opts,
|
|
478
631
|
readStdin: opts.readStdin ?? readProcessStdin
|
|
479
632
|
};
|
|
480
633
|
const codec = await buildCodec(variant, brand ?? "", values, optsWithStdin);
|
|
481
|
-
if (
|
|
482
|
-
opts.stderr(codec + "\n");
|
|
483
|
-
return 1;
|
|
634
|
+
if (isCodecError(codec)) {
|
|
635
|
+
opts.stderr(codec.message + "\n");
|
|
636
|
+
return codec.kind === "usage" ? 2 : 1;
|
|
484
637
|
}
|
|
485
638
|
for (let i = 0; i < count; i++) opts.stdout(await codec.generate() + "\n");
|
|
486
639
|
return 0;
|
|
487
640
|
}
|
|
488
641
|
//#endregion
|
|
489
|
-
//#region src/cli/usage.ts
|
|
490
|
-
function usage() {
|
|
491
|
-
return [
|
|
492
|
-
"Usage: ids <subcommand> [args]",
|
|
493
|
-
"",
|
|
494
|
-
"Subcommands:",
|
|
495
|
-
" inspect, i <id> [--opaque] [--wrapped --kind u32|i32|u64|i64] [--reverse] [--signed] [--key-format hex|base64url]",
|
|
496
|
-
" Decode an ID and print brand, timestamp (or lookup key), and canonical form.",
|
|
497
|
-
" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
|
|
498
|
-
" --wrapped reads the wrapping key from IDS_WRAPPING_KEY (hex by default; IDS_WRAPPING_KEY_FORMAT or --key-format).",
|
|
499
|
-
" --kind is required with --wrapped: u32, i32, u64, or i64.",
|
|
500
|
-
" --reverse decodes a Reverse Timestamp ID (newest-first sort order).",
|
|
501
|
-
" --signed decodes a Signed Timestamp ID; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
502
|
-
" Without IDS_SIGNING_KEY, --signed prints the timestamp only (no verification). With IDS_SIGNING_KEY, prints verification: ok or failed.",
|
|
503
|
-
" Note: --digest is not supported for inspect (Digest IDs are one-way; there is no reverse path).",
|
|
504
|
-
" generate, g <brand> [--count, -c N] [--opaque] [--reverse] [--signed] [--digest --ns <ns>] [--key-format hex|base64url]",
|
|
505
|
-
` Mint 1..${maxGenerateCount} canonical IDs for the given brand.`,
|
|
506
|
-
" --opaque reads the AES key from IDS_KEY (hex by default; IDS_KEY_FORMAT or --key-format).",
|
|
507
|
-
" --reverse mints Reverse Timestamp IDs (newest-first sort order).",
|
|
508
|
-
" --signed mints Signed Timestamp IDs; reads signing key from IDS_SIGNING_KEY (hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
509
|
-
" --digest mints a deterministic Digest ID from material read on stdin.",
|
|
510
|
-
" --ns <ns> is required: the namespace domain separator (non-secret, non-empty).",
|
|
511
|
-
" Reads the digest key from IDS_DIGEST_KEY (hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).",
|
|
512
|
-
" Same material + ns + key always produces the same ID. Digest IDs are one-way.",
|
|
513
|
-
" --count N > 1 is rejected: same material always produces the same ID.",
|
|
514
|
-
" keygen, k [--wrapped] [--signed] [--digest] [--bits 128|192|256] [--key-format hex|base64url]",
|
|
515
|
-
" Emit a random key for importOpaqueKey, importWrappingKey, importSigningKey, or importDigestKey (stdout only).",
|
|
516
|
-
" --wrapped emits a wrapping key for importWrappingKey instead (IDS_WRAPPING_KEY).",
|
|
517
|
-
" --signed emits a signing key for importSigningKey instead (IDS_SIGNING_KEY; hex by default; IDS_SIGNING_KEY_FORMAT or --key-format).",
|
|
518
|
-
" --digest emits a digest key for importDigestKey instead (IDS_DIGEST_KEY; hex by default; IDS_DIGEST_KEY_FORMAT or --key-format).",
|
|
519
|
-
""
|
|
520
|
-
].join("\n");
|
|
521
|
-
}
|
|
522
|
-
//#endregion
|
|
523
642
|
//#region src/cli/commands/inspect.ts
|
|
524
643
|
async function runInspect(args, opts) {
|
|
644
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
645
|
+
opts.stdout(usageInspect());
|
|
646
|
+
return 0;
|
|
647
|
+
}
|
|
525
648
|
const allowedFlags = deriveAllowedFlags(inspectPolicy);
|
|
526
649
|
const selectorFlags = new Set(inspectPolicy.selectable.map((v) => v.flag).filter((f) => f !== void 0));
|
|
527
650
|
const { flags, values, positionals, errors } = splitFlags(args, new Set([...allowedFlags].filter((f) => !selectorFlags.has(f))));
|
|
528
651
|
const unsupported = unsupportedFlagForCommand("inspect", flags, allowedFlags);
|
|
529
652
|
if (unsupported !== void 0) {
|
|
530
653
|
opts.stderr(unsupported + "\n");
|
|
531
|
-
return
|
|
654
|
+
return 2;
|
|
532
655
|
}
|
|
533
656
|
if (errors[0] !== void 0) {
|
|
534
657
|
opts.stderr(errors[0] + "\n");
|
|
535
|
-
return
|
|
658
|
+
return 2;
|
|
536
659
|
}
|
|
537
660
|
const [input] = positionals;
|
|
538
661
|
if (input === void 0) {
|
|
539
|
-
opts.stderr(
|
|
540
|
-
return
|
|
662
|
+
opts.stderr(usageInspect());
|
|
663
|
+
return 2;
|
|
541
664
|
}
|
|
542
665
|
const extra = positionals[1];
|
|
543
666
|
if (extra !== void 0) {
|
|
544
667
|
opts.stderr(`unexpected argument: ${extra}\n`);
|
|
545
|
-
return
|
|
668
|
+
return 2;
|
|
546
669
|
}
|
|
547
670
|
const variant = resolveVariant(inspectPolicy, flags);
|
|
548
671
|
if (typeof variant === "string") {
|
|
549
672
|
opts.stderr(variant + "\n");
|
|
550
|
-
return
|
|
673
|
+
return 2;
|
|
551
674
|
}
|
|
552
675
|
if (variant.key === void 0 && flags.has("--key-format")) {
|
|
553
676
|
opts.stderr("--key-format requires --opaque, --wrapped, or --signed\n");
|
|
554
|
-
return
|
|
677
|
+
return 2;
|
|
555
678
|
}
|
|
556
679
|
const brand = input.slice(0, 3).toLowerCase();
|
|
680
|
+
const cap = variant.inspect;
|
|
557
681
|
let verifyTimestamp;
|
|
558
682
|
let verifyCanonical;
|
|
559
683
|
let verifyNowMs;
|
|
560
|
-
if (
|
|
684
|
+
if (cap.mode === "verify") {
|
|
561
685
|
const fmtCheck = parseKeyFormat(values, opts, variant.key);
|
|
562
686
|
if (isKeyFormatError(fmtCheck)) {
|
|
563
687
|
opts.stderr(fmtCheck + "\n");
|
|
564
|
-
return
|
|
688
|
+
return 2;
|
|
565
689
|
}
|
|
566
690
|
let tsCodec;
|
|
567
691
|
try {
|
|
@@ -572,7 +696,7 @@ async function runInspect(args, opts) {
|
|
|
572
696
|
}
|
|
573
697
|
const structValidation = tsCodec["~standard"].validate(input);
|
|
574
698
|
if (structValidation.issues) {
|
|
575
|
-
opts.stderr(structValidation.issues[0].message + "\n");
|
|
699
|
+
opts.stderr(invalidIdPrefix + structValidation.issues[0].message + "\n");
|
|
576
700
|
return 1;
|
|
577
701
|
}
|
|
578
702
|
verifyCanonical = structValidation.value;
|
|
@@ -580,32 +704,36 @@ async function runInspect(args, opts) {
|
|
|
580
704
|
verifyNowMs = (opts.now ?? Date.now)();
|
|
581
705
|
}
|
|
582
706
|
const codecOrError = await buildCodec(variant, brand, values, opts);
|
|
583
|
-
if (
|
|
584
|
-
if (
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
707
|
+
if (isCodecError(codecOrError)) {
|
|
708
|
+
if (cap.mode === "verify") {
|
|
709
|
+
opts.stdout(formatSignedInspectOutput({
|
|
710
|
+
brand,
|
|
711
|
+
timestamp: verifyTimestamp,
|
|
712
|
+
canonical: verifyCanonical,
|
|
713
|
+
input,
|
|
714
|
+
nowMs: verifyNowMs,
|
|
715
|
+
verification: "unavailable"
|
|
716
|
+
}));
|
|
717
|
+
opts.stderr(codecOrError.message + "\n");
|
|
718
|
+
return 1;
|
|
719
|
+
}
|
|
720
|
+
opts.stderr(codecOrError.message + "\n");
|
|
721
|
+
return codecOrError.kind === "usage" ? 2 : 1;
|
|
594
722
|
}
|
|
595
723
|
let canonical;
|
|
596
|
-
if (
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
599
|
-
opts.stderr(
|
|
724
|
+
if (cap.mode !== "verify" && cap.mode !== "unsupported") {
|
|
725
|
+
const parsed = cap.validate(codecOrError, input);
|
|
726
|
+
if ("issue" in parsed) {
|
|
727
|
+
opts.stderr(parsed.issue + "\n");
|
|
600
728
|
return 1;
|
|
601
729
|
}
|
|
602
|
-
canonical =
|
|
730
|
+
canonical = parsed.value;
|
|
603
731
|
}
|
|
604
|
-
switch (
|
|
732
|
+
switch (cap.mode) {
|
|
605
733
|
case "readable": {
|
|
606
|
-
const timestamp =
|
|
734
|
+
const timestamp = cap.extractTimestamp(codecOrError, canonical);
|
|
607
735
|
const nowMs = (opts.now ?? Date.now)();
|
|
608
|
-
opts.stderr(
|
|
736
|
+
opts.stderr(cap.note + "\n");
|
|
609
737
|
opts.stdout(formatInspectOutput({
|
|
610
738
|
brand,
|
|
611
739
|
timestamp,
|
|
@@ -616,9 +744,9 @@ async function runInspect(args, opts) {
|
|
|
616
744
|
return 0;
|
|
617
745
|
}
|
|
618
746
|
case "keyed-readable": {
|
|
619
|
-
const timestamp = await
|
|
747
|
+
const timestamp = await cap.extractTimestamp(codecOrError, canonical);
|
|
620
748
|
const nowMs = (opts.now ?? Date.now)();
|
|
621
|
-
opts.stderr(
|
|
749
|
+
opts.stderr(cap.note + "\n");
|
|
622
750
|
opts.stdout(formatInspectOutput({
|
|
623
751
|
brand,
|
|
624
752
|
timestamp,
|
|
@@ -631,7 +759,7 @@ async function runInspect(args, opts) {
|
|
|
631
759
|
case "unwrap": {
|
|
632
760
|
let lookupKey;
|
|
633
761
|
try {
|
|
634
|
-
lookupKey = await
|
|
762
|
+
lookupKey = await cap.unwrap(codecOrError, canonical);
|
|
635
763
|
} catch (err) {
|
|
636
764
|
opts.stderr(formatCliError(err) + "\n");
|
|
637
765
|
return 1;
|
|
@@ -645,7 +773,7 @@ async function runInspect(args, opts) {
|
|
|
645
773
|
return 0;
|
|
646
774
|
}
|
|
647
775
|
case "verify": {
|
|
648
|
-
const verifyResult = await
|
|
776
|
+
const verifyResult = await cap.safeVerify(codecOrError, input);
|
|
649
777
|
if (!verifyResult.ok) {
|
|
650
778
|
/* v8 ignore next 4 -- defensive: both codecs share the same wire parse so ParseError
|
|
651
779
|
is unreachable after the createTimestampId pre-validation above passes */
|
|
@@ -684,38 +812,42 @@ async function runInspect(args, opts) {
|
|
|
684
812
|
}
|
|
685
813
|
//#endregion
|
|
686
814
|
//#region src/cli/commands/keygen.ts
|
|
687
|
-
function runKeygen(args, opts) {
|
|
815
|
+
async function runKeygen(args, opts) {
|
|
816
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
817
|
+
opts.stdout(usageKeygen());
|
|
818
|
+
return Promise.resolve(0);
|
|
819
|
+
}
|
|
688
820
|
const allowedFlags = deriveAllowedFlags(keygenPolicy);
|
|
689
821
|
const variantExtraFlags = new Set(keygenPolicy.selectable.flatMap((v) => v.extraFlags ?? []));
|
|
690
822
|
const { flags, values, positionals, errors } = splitFlags(args, allowedFlags);
|
|
691
823
|
const unsupported = unsupportedFlagForCommand("keygen", flags, new Set([...allowedFlags].filter((f) => !variantExtraFlags.has(f))));
|
|
692
824
|
if (unsupported !== void 0) {
|
|
693
825
|
opts.stderr(unsupported + "\n");
|
|
694
|
-
return Promise.resolve(
|
|
826
|
+
return Promise.resolve(2);
|
|
695
827
|
}
|
|
696
828
|
if (errors[0] !== void 0) {
|
|
697
829
|
opts.stderr(errors[0] + "\n");
|
|
698
|
-
return Promise.resolve(
|
|
830
|
+
return Promise.resolve(2);
|
|
699
831
|
}
|
|
700
832
|
const extra = positionals[0];
|
|
701
833
|
if (extra !== void 0) {
|
|
702
834
|
opts.stderr(`unexpected argument: ${extra}\n`);
|
|
703
|
-
return Promise.resolve(
|
|
835
|
+
return Promise.resolve(2);
|
|
704
836
|
}
|
|
705
837
|
const variant = resolveVariant(keygenPolicy, flags);
|
|
706
838
|
if (typeof variant === "string") {
|
|
707
839
|
opts.stderr(variant + "\n");
|
|
708
|
-
return Promise.resolve(
|
|
840
|
+
return Promise.resolve(2);
|
|
709
841
|
}
|
|
710
842
|
const bits = parseBits(values);
|
|
711
843
|
if (typeof bits === "string") {
|
|
712
844
|
opts.stderr(bits + "\n");
|
|
713
|
-
return Promise.resolve(
|
|
845
|
+
return Promise.resolve(2);
|
|
714
846
|
}
|
|
715
847
|
const format = parseKeyFormatFromFlag(values);
|
|
716
848
|
if (isKeyFormatError(format)) {
|
|
717
849
|
opts.stderr(format + "\n");
|
|
718
|
-
return Promise.resolve(
|
|
850
|
+
return Promise.resolve(2);
|
|
719
851
|
}
|
|
720
852
|
/* v8 ignore next 4 -- defensive guard; all keygenPolicy variants have key defined */
|
|
721
853
|
if (variant.key === void 0) {
|
|
@@ -724,6 +856,7 @@ function runKeygen(args, opts) {
|
|
|
724
856
|
}
|
|
725
857
|
const bytes = new Uint8Array(bits / 8);
|
|
726
858
|
crypto.getRandomValues(bytes);
|
|
859
|
+
opts.stderr("Warning: secret key material — redirect to a file (chmod 0600) and avoid shell history.\n");
|
|
727
860
|
opts.stdout(variant.key.encode(bytes, format) + "\n");
|
|
728
861
|
return Promise.resolve(0);
|
|
729
862
|
}
|
|
@@ -744,15 +877,20 @@ const commands = [
|
|
|
744
877
|
}
|
|
745
878
|
];
|
|
746
879
|
async function run(opts) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
880
|
+
try {
|
|
881
|
+
const [subcommand, ...rest] = opts.argv;
|
|
882
|
+
const command = commands.find((candidate) => candidate.names.includes(subcommand ?? ""));
|
|
883
|
+
if (command !== void 0) return await command.run(rest, opts);
|
|
884
|
+
if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h") {
|
|
885
|
+
opts.stdout(usage());
|
|
886
|
+
return 0;
|
|
887
|
+
}
|
|
888
|
+
opts.stderr(usage());
|
|
889
|
+
return 2;
|
|
890
|
+
} catch (err) {
|
|
891
|
+
opts.stderr(formatCliError(err) + "\n");
|
|
892
|
+
return 1;
|
|
753
893
|
}
|
|
754
|
-
opts.stderr(usage());
|
|
755
|
-
return 1;
|
|
756
894
|
}
|
|
757
895
|
//#endregion
|
|
758
896
|
//#region bin/cli.ts
|