@sdxc/uuid 2026.10.13 → 2026.11.3

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 CHANGED
@@ -79,6 +79,24 @@ try {
79
79
  }
80
80
  ```
81
81
 
82
+ ## Command line
83
+
84
+ `sdxc-uuid` generates and checks UUIDs from a terminal or a script, with no install step:
85
+
86
+ ```sh
87
+ bunx @sdxc/uuid --help
88
+ ```
89
+
90
+ With [`@sdxc/kit`](https://www.npmjs.com/package/@sdxc/kit) installed, `sdxc pkg uuid …` runs the same command line, using the version your project installs.
91
+
92
+ `validate` reads the UUID from its argument, or stdin when it is `-` or absent, prints its version and, for v7, its creation time, and exits 1 when the value is invalid. `--json` prints the result as data.
93
+
94
+ ```sh
95
+ bunx @sdxc/uuid v4 -n 5
96
+ bunx @sdxc/uuid v7 -n 1000 > ids.txt
97
+ bunx @sdxc/uuid validate 019a6f3e-5c21-7a4b-9d3e-8f1a2b3c4d5e --json
98
+ ```
99
+
82
100
  ## API
83
101
 
84
102
  ### `isUUID(value: string): value is UUID`
@@ -91,6 +109,10 @@ Accepts exactly the values `assertUUID` accepts.
91
109
  Narrows the value to `UUID`, or throws `InvalidUUIDTypeError` for a non-string,
92
110
  `InvalidUUIDLengthError` for a length other than 36, and `InvalidUUIDFormatError` otherwise.
93
111
 
112
+ ### `getVersion(id: UUID): number`
113
+
114
+ Returns the version the UUID declares, such as `4` or `7`.
115
+
94
116
  ### `generateUUID(): UUID` from `@sdxc/uuid/v7`
95
117
 
96
118
  Returns a version 7 UUID ([RFC 9562](https://www.rfc-editor.org/rfc/rfc9562)): a 48-bit
@@ -98,6 +120,10 @@ millisecond timestamp, a 12-bit counter and 62 random bits. Each value sorts aft
98
120
  one from the same process, including within one millisecond. Anyone holding the value can read
99
121
  its creation time.
100
122
 
123
+ ### `getTimestamp(id: UUID): number` from `@sdxc/uuid/v7`
124
+
125
+ Returns the Unix millisecond timestamp a v7 UUID was generated at.
126
+
101
127
  ### `generateUUID(): UUID` from `@sdxc/uuid/v4`
102
128
 
103
129
  Returns a version 4 UUID from `crypto.randomUUID()`: 122 random bits, with no ordering and no
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The `sdxc-uuid` command line as a contract: `v4` and `v7` generate identifiers, `validate`
3
+ * checks one and reports its version. Help, usage errors and each handler's typed `ctx.flags`
4
+ * come from this one declaration.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ /** Every command `sdxc-uuid` takes. */
10
+ export declare const UUID_COMMANDS: import("@sdxc/cli").Group<Omit<{
11
+ readonly description: "sdxc-uuid — generate and validate UUIDs";
12
+ readonly v4: import("@sdxc/cli").Command<Readonly<Record<string, import("@sdxc/cli").Argument<unknown>>>, {
13
+ readonly count: import("@sdxc/cli").ValueFlag<number, true, false, false>;
14
+ }>;
15
+ readonly v7: import("@sdxc/cli").Command<Readonly<Record<string, import("@sdxc/cli").Argument<unknown>>>, {
16
+ readonly count: import("@sdxc/cli").ValueFlag<number, true, false, false>;
17
+ }>;
18
+ readonly validate: import("@sdxc/cli").Command<{
19
+ readonly uuid: import("@sdxc/cli").Argument<string | undefined>;
20
+ }, {
21
+ readonly json: import("@sdxc/cli").Flag<boolean>;
22
+ }>;
23
+ }, "description" | "hidden" | "arguments" | "flags" | "tasks">, Record<never, never>, Record<never, never>>;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The `sdxc-uuid` command line as a contract: `v4` and `v7` generate identifiers, `validate`
3
+ * checks one and reports its version. Help, usage errors and each handler's typed `ctx.flags`
4
+ * come from this one declaration.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import { argument, command, commands, flag } from "@sdxc/cli";
10
+ import * as s from "remix/data-schema";
11
+ /** The most identifiers one run prints, so a typo in `-n` cannot flood a terminal. */
12
+ const MAX_COUNT = 100_000;
13
+ /**
14
+ * A count from 1 to {@link MAX_COUNT}, written as digits only, so `+8`, `8.0` and `0x8` are
15
+ * usage errors.
16
+ */
17
+ function count() {
18
+ return s.createSchema(function validate(value, context) {
19
+ if (typeof value === "string" && /^\d+$/.test(value)) {
20
+ let parsed = Number(value);
21
+ if (parsed >= 1 && parsed <= MAX_COUNT)
22
+ return { value: parsed };
23
+ }
24
+ return s.fail(`expects an integer from 1 to ${MAX_COUNT}; got ${JSON.stringify(value)}`, context.path);
25
+ });
26
+ }
27
+ /** The `-n` flag both generators take: how many identifiers to print, one per line. */
28
+ const COUNT = flag.string("count", {
29
+ short: "n",
30
+ valueName: "N",
31
+ default: 1,
32
+ schema: count(),
33
+ description: "How many to print, one per line",
34
+ });
35
+ /** Every command `sdxc-uuid` takes. */
36
+ export const UUID_COMMANDS = commands({
37
+ description: "sdxc-uuid — generate and validate UUIDs",
38
+ v4: command({
39
+ description: "Print random version 4 UUIDs, which reveal nothing about when they were made.",
40
+ flags: { count: COUNT },
41
+ examples: ["v4", "v4 -n 5"],
42
+ }),
43
+ v7: command({
44
+ description: "Print time-ordered version 7 UUIDs; each sorts after the one before, within a millisecond too.",
45
+ flags: { count: COUNT },
46
+ examples: ["v7", "v7 -n 1000 > ids.txt"],
47
+ }),
48
+ validate: command({
49
+ description: "Check a UUID is lowercase 8-4-4-4-12 hex and print its version, plus its creation time for v7. Exits 1 when it is invalid.",
50
+ arguments: {
51
+ uuid: argument(s.string(), {
52
+ optional: true,
53
+ description: "The UUID to check; stdin when it is - or absent",
54
+ }),
55
+ },
56
+ flags: { json: flag.boolean("json", { description: "Print the result as JSON" }) },
57
+ examples: ["validate 019a6f3e-5c21-7a4b-9d3e-8f1a2b3c4d5e", "validate --json < id.txt"],
58
+ }),
59
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The `sdxc-uuid v4` handler: prints random version 4 UUIDs from the package's own generator,
3
+ * one per line, for ids that must not reveal when they were created.
4
+ *
5
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
6
+ * @copyright Sergio Xalambrí 2026
7
+ */
8
+ /** Prints `--count` values, each on its own line. */
9
+ declare const _default: import("@sdxc/cli").CommandAction<import("@sdxc/cli").Command<Readonly<Record<string, import("@sdxc/cli").Argument<unknown>>>, {
10
+ readonly count: import("@sdxc/cli").ValueFlag<number, true, false, false>;
11
+ }>, []>;
12
+ export default _default;
package/dist/cli-v4.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The `sdxc-uuid v4` handler: prints random version 4 UUIDs from the package's own generator,
3
+ * one per line, for ids that must not reveal when they were created.
4
+ *
5
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
6
+ * @copyright Sergio Xalambrí 2026
7
+ */
8
+ import { createCommand, success } from "@sdxc/cli";
9
+ import { UUID_COMMANDS } from "./cli-contract.js";
10
+ import { generateUUID } from "./v4.js";
11
+ /** Prints `--count` values, each on its own line. */
12
+ export default createCommand(UUID_COMMANDS.v4, (ctx) => {
13
+ let ids = Array.from({ length: ctx.flags.count }, () => generateUUID());
14
+ ctx.stdout(`${ids.join("\n")}\n`);
15
+ return success();
16
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The `sdxc-uuid v7` handler: prints time-ordered version 7 UUIDs, one per line, in the order
3
+ * they sort, so a batch can seed a table or fixture already in index order.
4
+ *
5
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
6
+ * @copyright Sergio Xalambrí 2026
7
+ */
8
+ /** Prints `--count` values, each on its own line and each sorting after the line above. */
9
+ declare const _default: import("@sdxc/cli").CommandAction<import("@sdxc/cli").Command<Readonly<Record<string, import("@sdxc/cli").Argument<unknown>>>, {
10
+ readonly count: import("@sdxc/cli").ValueFlag<number, true, false, false>;
11
+ }>, []>;
12
+ export default _default;
package/dist/cli-v7.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The `sdxc-uuid v7` handler: prints time-ordered version 7 UUIDs, one per line, in the order
3
+ * they sort, so a batch can seed a table or fixture already in index order.
4
+ *
5
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
6
+ * @copyright Sergio Xalambrí 2026
7
+ */
8
+ import { createCommand, success } from "@sdxc/cli";
9
+ import { UUID_COMMANDS } from "./cli-contract.js";
10
+ import { generateUUID } from "./v7.js";
11
+ /** Prints `--count` values, each on its own line and each sorting after the line above. */
12
+ export default createCommand(UUID_COMMANDS.v7, (ctx) => {
13
+ let ids = Array.from({ length: ctx.flags.count }, () => generateUUID());
14
+ ctx.stdout(`${ids.join("\n")}\n`);
15
+ return success();
16
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The `sdxc-uuid validate` handler: checks one UUID from an argument or stdin with the
3
+ * package's `assertUUID`, and reports its version and, for v7, the time it was generated.
4
+ * An invalid value exits 1 with the reason, so a script can branch on the exit code.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ /**
10
+ * Prints `valid`, the version and the v7 creation time, or `invalid` and the reason; `--json`
11
+ * prints `{ valid, uuid, version, timestamp? }` or `{ valid, uuid, error }`. Stdin loses one
12
+ * trailing line ending, so `echo $ID |` checks the id itself.
13
+ */
14
+ declare const _default: import("@sdxc/cli").CommandAction<import("@sdxc/cli").Command<{
15
+ readonly uuid: import("@sdxc/cli").Argument<string | undefined>;
16
+ }, {
17
+ readonly json: import("@sdxc/cli").Flag<boolean>;
18
+ }>, []>;
19
+ export default _default;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The `sdxc-uuid validate` handler: checks one UUID from an argument or stdin with the
3
+ * package's `assertUUID`, and reports its version and, for v7, the time it was generated.
4
+ * An invalid value exits 1 with the reason, so a script can branch on the exit code.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import { createCommand, exit, success, usage } from "@sdxc/cli";
10
+ import { isFailure, wrap } from "@sdxc/result";
11
+ import { UUID_COMMANDS } from "./cli-contract.js";
12
+ import { getTimestamp } from "./v7.js";
13
+ import { assertUUID, getVersion } from "./index.js";
14
+ /**
15
+ * Prints `valid`, the version and the v7 creation time, or `invalid` and the reason; `--json`
16
+ * prints `{ valid, uuid, version, timestamp? }` or `{ valid, uuid, error }`. Stdin loses one
17
+ * trailing line ending, so `echo $ID |` checks the id itself.
18
+ */
19
+ export default createCommand(UUID_COMMANDS.validate, async (ctx) => {
20
+ let value = ctx.arguments.uuid ?? "-";
21
+ if (value === "-") {
22
+ if (ctx.stdin.isTTY)
23
+ return usage("Pass a UUID, or pipe one on stdin");
24
+ value = (await ctx.stdin.text()).replace(/\r?\n$/, "");
25
+ }
26
+ let checked = wrap(() => {
27
+ assertUUID(value);
28
+ return value;
29
+ });
30
+ if (isFailure(checked)) {
31
+ ctx.stdout(ctx.flags.json
32
+ ? `${JSON.stringify({ valid: false, uuid: value, error: checked.error.message })}\n`
33
+ : `invalid: ${checked.error.message}\n`);
34
+ return exit(1);
35
+ }
36
+ let uuid = checked.data;
37
+ let version = getVersion(uuid);
38
+ let timestamp = version === 7 ? new Date(getTimestamp(uuid)).toISOString() : undefined;
39
+ if (ctx.flags.json) {
40
+ ctx.stdout(`${JSON.stringify({ valid: true, uuid, version, timestamp })}\n`);
41
+ return success();
42
+ }
43
+ let lines = ["valid", `version ${version}`];
44
+ if (timestamp !== undefined)
45
+ lines.push(`timestamp ${timestamp}`);
46
+ ctx.stdout(`${lines.join("\n")}\n`);
47
+ return success();
48
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `sdxc-uuid` command-line program: the contract in `cli-contract.ts` with each command
4
+ * mapped onto its handler, loaded only when that command runs. Exit codes: 0 done, 1 the UUID
5
+ * is invalid, 2 usage or unreadable input.
6
+ *
7
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
8
+ * @copyright Sergio Xalambrí 2026
9
+ */
10
+ /** The `sdxc-uuid` program, run by the bin entry below and by tests through `invoke()`. */
11
+ export declare const cli: import("@sdxc/cli").CLI<import("@sdxc/cli").Group<Omit<{
12
+ readonly description: "sdxc-uuid — generate and validate UUIDs";
13
+ readonly v4: import("@sdxc/cli").Command<Readonly<Record<string, import("@sdxc/cli").Argument<unknown>>>, {
14
+ readonly count: import("@sdxc/cli").ValueFlag<number, true, false, false>;
15
+ }>;
16
+ readonly v7: import("@sdxc/cli").Command<Readonly<Record<string, import("@sdxc/cli").Argument<unknown>>>, {
17
+ readonly count: import("@sdxc/cli").ValueFlag<number, true, false, false>;
18
+ }>;
19
+ readonly validate: import("@sdxc/cli").Command<{
20
+ readonly uuid: import("@sdxc/cli").Argument<string | undefined>;
21
+ }, {
22
+ readonly json: import("@sdxc/cli").Flag<boolean>;
23
+ }>;
24
+ }, "description" | "hidden" | "arguments" | "flags" | "tasks">, Record<never, never>, Record<never, never>>, []>;
package/dist/cli.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `sdxc-uuid` command-line program: the contract in `cli-contract.ts` with each command
4
+ * mapped onto its handler, loaded only when that command runs. Exit codes: 0 done, 1 the UUID
5
+ * is invalid, 2 usage or unreadable input.
6
+ *
7
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
8
+ * @copyright Sergio Xalambrí 2026
9
+ */
10
+ import { createCLI, lazy } from "@sdxc/cli";
11
+ import { UUID_COMMANDS } from "./cli-contract.js";
12
+ /** The `sdxc-uuid` program, run by the bin entry below and by tests through `invoke()`. */
13
+ export const cli = createCLI({ name: "sdxc-uuid", commands: UUID_COMMANDS });
14
+ cli.map(UUID_COMMANDS.v4, lazy(() => import("./cli-v4.js")));
15
+ cli.map(UUID_COMMANDS.v7, lazy(() => import("./cli-v7.js")));
16
+ cli.map(UUID_COMMANDS.validate, lazy(() => import("./cli-validate.js")));
17
+ if (import.meta.main) {
18
+ process.exit(await cli.run(process.argv.slice(2)));
19
+ }
package/dist/index.d.ts CHANGED
@@ -9,4 +9,14 @@ export type UUID = BaseUUID & {
9
9
  };
10
10
  export declare function isUUID(value: string): value is UUID;
11
11
  export declare function assertUUID(value: string): asserts value is UUID;
12
+ /**
13
+ * The version a UUID declares in its 13th hex digit, so a caller can tell a time-ordered v7
14
+ * from a random v4 before reading any version-specific field.
15
+ *
16
+ * @param id - A validated UUID
17
+ * @returns The version number, 0 to 15.
18
+ * @example
19
+ * getVersion(generateUUID()); // 7, from "@sdxc/uuid/v7"
20
+ */
21
+ export declare function getVersion(id: UUID): number;
12
22
  export { InvalidUUIDFormatError, InvalidUUIDLengthError, InvalidUUIDTypeError };
package/dist/index.js CHANGED
@@ -21,4 +21,16 @@ export function assertUUID(value) {
21
21
  throw new InvalidUUIDFormatError(value);
22
22
  }
23
23
  }
24
+ /**
25
+ * The version a UUID declares in its 13th hex digit, so a caller can tell a time-ordered v7
26
+ * from a random v4 before reading any version-specific field.
27
+ *
28
+ * @param id - A validated UUID
29
+ * @returns The version number, 0 to 15.
30
+ * @example
31
+ * getVersion(generateUUID()); // 7, from "@sdxc/uuid/v7"
32
+ */
33
+ export function getVersion(id) {
34
+ return Number.parseInt(id[14], 16);
35
+ }
24
36
  export { InvalidUUIDFormatError, InvalidUUIDLengthError, InvalidUUIDTypeError };
package/dist/v7.d.ts CHANGED
@@ -19,3 +19,14 @@ import type { UUID } from "./index.js";
19
19
  * first < second; // true
20
20
  */
21
21
  export declare function generateUUID(): UUID;
22
+ /**
23
+ * The Unix millisecond timestamp a version 7 UUID starts with: the moment it was generated.
24
+ * The first 48 bits carry it for v7 only, so check `getVersion` first for a UUID of
25
+ * unknown origin.
26
+ *
27
+ * @param id - A version 7 UUID
28
+ * @returns Milliseconds since the Unix epoch.
29
+ * @example
30
+ * new Date(getTimestamp(generateUUID())); // now
31
+ */
32
+ export declare function getTimestamp(id: UUID): number;
package/dist/v7.js CHANGED
@@ -42,6 +42,19 @@ export function generateUUID() {
42
42
  assertUUID(id);
43
43
  return id;
44
44
  }
45
+ /**
46
+ * The Unix millisecond timestamp a version 7 UUID starts with: the moment it was generated.
47
+ * The first 48 bits carry it for v7 only, so check `getVersion` first for a UUID of
48
+ * unknown origin.
49
+ *
50
+ * @param id - A version 7 UUID
51
+ * @returns Milliseconds since the Unix epoch.
52
+ * @example
53
+ * new Date(getTimestamp(generateUUID())); // now
54
+ */
55
+ export function getTimestamp(id) {
56
+ return Number.parseInt(id.slice(0, 8) + id.slice(9, 13), 16);
57
+ }
45
58
  /**
46
59
  * Moves the clock to `now`, or past the last timestamp when `now` is not later than it.
47
60
  * A new millisecond seeds the counter randomly below its midpoint, leaving at least 2048
package/package.json CHANGED
@@ -1,15 +1,23 @@
1
1
  {
2
2
  "name": "@sdxc/uuid",
3
- "version": "2026.10.13",
3
+ "version": "2026.11.3",
4
4
  "description": "Validate and assert UUIDs behind a branded UUID type, and generate v4 or v7 values",
5
5
  "license": "MIT",
6
+ "bin": {
7
+ "sdxc-uuid": "dist/cli.js"
8
+ },
6
9
  "type": "module",
7
10
  "exports": {
8
11
  ".": "./dist/index.js",
9
12
  "./v4": "./dist/v4.js",
10
13
  "./v7": "./dist/v7.js"
11
14
  },
12
- "gitHead": "5ff758a57e35480a8db7abd24f1d9527b81bfb51",
15
+ "dependencies": {
16
+ "@sdxc/cli": "2026.11.3",
17
+ "@sdxc/result": "2026.10.13",
18
+ "remix": "3.1.0"
19
+ },
20
+ "gitHead": "d1dd4030f8df68b8a170940a3f790ea8654d3676",
13
21
  "publishConfig": {
14
22
  "access": "public"
15
23
  },
@@ -18,7 +26,7 @@
18
26
  "url": "git+https://github.com/sergiodxa/monorepo.git",
19
27
  "directory": "packages/uuid"
20
28
  },
21
- "homepage": "https://sdxc.sergiodxa.com/api/uuid",
29
+ "homepage": "https://sdxc.dev/api/uuid",
22
30
  "author": {
23
31
  "name": "Sergio Xalambrí",
24
32
  "url": "https://sergiodxa.com"