From 4a6b7d64103427eab15d76416d7c12ab1505ecbd Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 10:24:25 -0700 Subject: [PATCH 01/15] fix(cjs): load the CommonJS entry without top-level await index.cjs loads index.mjs through Node's synchronous require(esm), and index.mjs used top-level await for the devcheck and the library import, so every require("@cldmv/uuid") failed with ERR_REQUIRE_ASYNC_MODULE. - index.mjs: devcheck runs inside an async function (it is never published, so a failed import is ignored) and the library is a static import, so the module graph has no top-level await. - index.cjs: a plain require of index.mjs. Where Node.js has no require(esm) (before 20.19 / 22.12) it throws ERR_REQUIRE_ESM with a message pointing to import() instead of a bare loader error. - tests/cjs: node:test checks run by `npm test` and `npm run coverage` after Vitest: require() returns the same objects as import, and the version check fires when require(esm) is off. Fixes #49 --- index.cjs | 16 +++++++++--- index.mjs | 20 +++++++++------ package.json | 5 ++-- tests/cjs/entry.test.cjs | 54 ++++++++++++++++++++++++++++++++++++++++ types/index.d.mts | 16 +++--------- types/index.d.mts.map | 2 +- 6 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 tests/cjs/entry.test.cjs diff --git a/index.cjs b/index.cjs index 4fc2d64..a268096 100644 --- a/index.cjs +++ b/index.cjs @@ -21,10 +21,20 @@ * * @module uuid */ -const { createRequire } = require("module"); -const requireESM = createRequire(__filename); +"use strict"; -const { UUID, uuid, ISSUER_CATEGORIES } = requireESM("./index.mjs"); +// index.cjs is a thin wrapper: it loads index.mjs through Node's synchronous require(esm). +// Node.js versions without require(esm) would fail with a bare ERR_REQUIRE_ESM, so fail +// early with a message that says what to do instead. +if (!process.features?.require_module) { + const error = new Error( + `@cldmv/uuid: require() needs Node.js ^20.19.0 or >=22.12.0 (this is ${process.version}). On older Node.js, load the package with import() instead.` + ); + error.code = "ERR_REQUIRE_ESM"; + throw error; +} + +const { UUID, uuid, ISSUER_CATEGORIES } = require("./index.mjs"); // Export UUID as default module.exports = UUID; diff --git a/index.mjs b/index.mjs index 1214d6c..4c8f455 100644 --- a/index.mjs +++ b/index.mjs @@ -13,19 +13,25 @@ * */ -// Development environment check (must happen before UUID imports) -try { - await import("./devcheck.mjs"); -} catch { - // ignore -} +// Development environment check. devcheck.mjs exists only in a source checkout; it is +// never published, so the import is allowed to fail. It runs inside an async function +// rather than as a top-level await: index.cjs loads this file through Node's synchronous +// require(esm), which rejects any module graph containing top-level await +// (ERR_REQUIRE_ASYNC_MODULE). +(async () => { + try { + await import("./devcheck.mjs"); + } catch { + // ignore - devcheck.mjs is not published + } +})(); /** * ESM entry point for UUID * * Re-exports all components from the main UUID module */ -const { UUID, ISSUER_CATEGORIES } = await import("@cldmv/uuid/main"); +import { UUID, ISSUER_CATEGORIES } from "@cldmv/uuid/main"; export { UUID, UUID as uuid, ISSUER_CATEGORIES }; export default UUID; diff --git a/package.json b/package.json index 7fb92f4..4a021c6 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,10 @@ "build:dist": "node -e \"const fs = require('fs'); const path = require('path'); fs.rmSync('dist', {recursive: true, force: true}); fs.mkdirSync('dist', {recursive: true}); fs.cpSync('src', 'dist', {recursive: true});\"", "build:types": "node -e \"require('fs').rmSync('types', {recursive: true, force: true})\" && tsc --project .configs/tsconfig.dts.jsonc", "demo": "node scripts/demo-custom-uuids.mjs", - "test": "node tests/run-vitest.mjs", + "test": "node tests/run-vitest.mjs && npm run test:cjs", + "test:cjs": "node --conditions=uuid-dev --test tests/cjs/entry.test.cjs", "test:watch": "vitest --config .configs/vitest.config.mjs", - "coverage": "node tests/run-vitest.mjs --coverage-quiet", + "coverage": "node tests/run-vitest.mjs --coverage-quiet && npm run test:cjs", "ci:coverage": "npm run coverage", "fix:headers": "fix-headers --config .configs/fix-headers.json", "types:build": "tsc -p .configs/tsconfig.dts.jsonc --noCheck", diff --git a/tests/cjs/entry.test.cjs b/tests/cjs/entry.test.cjs new file mode 100644 index 0000000..1c25a39 --- /dev/null +++ b/tests/cjs/entry.test.cjs @@ -0,0 +1,54 @@ +/** + * + * @Project: @cldmv/uuid + * @Filename: /tests/cjs/entry.test.cjs + * @Date: 2026-10-03T10:18:01-07:00 (1791047881) + * @Author: Nate Corcoran + * @Email: + * ----- + * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-10-03T10:24:06-07:00 (1791048246) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + * + */ + +/** + * CommonJS entry tests. These run under Node's own test runner (`node --test`), not Vitest: + * Vitest loads files through its own module runner, so it cannot show whether a plain + * `require()` of the package works the way it does for a CommonJS consumer. + */ +"use strict"; + +const { test } = require("node:test"); +const assert = require("node:assert/strict"); +const { spawnSync } = require("node:child_process"); +const path = require("node:path"); + +const repoRoot = path.resolve(__dirname, "../.."); +const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; + +test("require() returns the same UUID object as import", async () => { + const UUID = require("../../index.cjs"); + const esm = await import("../../index.mjs"); + + assert.equal(UUID, esm.default); + assert.equal(UUID.UUID, esm.UUID); + assert.equal(UUID.uuid, esm.uuid); + assert.equal(UUID.ISSUER_CATEGORIES, esm.ISSUER_CATEGORIES); + assert.match(String(UUID.v4()), UUID_PATTERN); +}); + +test("require() fails with a clear message where Node.js has no require(esm)", () => { + // --no-experimental-require-module turns require(esm) off, which is what Node.js + // versions before 20.19 / 22.12 look like to the entry. + const res = spawnSync(process.execPath, ["--no-experimental-require-module", "-e", "require('./index.cjs')"], { + cwd: repoRoot, + encoding: "utf8" + }); + + assert.notEqual(res.status, 0); + assert.match(res.stderr, /ERR_REQUIRE_ESM/); + assert.match(res.stderr, /require\(\) needs Node\.js \^20\.19\.0 or >=22\.12\.0/); + assert.match(res.stderr, /import\(\)/); +}); diff --git a/types/index.d.mts b/types/index.d.mts index 5189d85..808e242 100644 --- a/types/index.d.mts +++ b/types/index.d.mts @@ -1,15 +1,5 @@ export default UUID; -export const UUID: typeof import("@cldmv/uuid/main").UUID; -export const ISSUER_CATEGORIES: { - UNASSIGNED: number; - DRAFTER_RESERVED: number; - CATEGORY_A_START: number; - CATEGORY_A_END: number; - CATEGORY_B_START: number; - CATEGORY_B_END: number; - SPEC_ORIGINATOR: number; - RFC_EXPANSION_START: number; - RFC_EXPANSION_END: number; -}; -export { UUID as uuid }; +import { UUID } from "@cldmv/uuid/main"; +import { ISSUER_CATEGORIES } from "@cldmv/uuid/main"; +export { UUID, UUID as uuid, ISSUER_CATEGORIES }; //# sourceMappingURL=index.d.mts.map \ No newline at end of file diff --git a/types/index.d.mts.map b/types/index.d.mts.map index 6d2496d..842020d 100644 --- a/types/index.d.mts.map +++ b/types/index.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mjs"],"names":[],"mappings":""} \ No newline at end of file +{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mjs"],"names":[],"mappings":";qBAiCwC,kBAAkB;kCAAlB,kBAAkB"} \ No newline at end of file From 646c97fa10a6a43d9b66e334f979517df63320cd Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 11:14:12 -0700 Subject: [PATCH 02/15] fix(types): export the default from the named export list tsc emitted `export default UUID;` above `import { UUID }` in types/index.d.mts, which CodeQL flags as js/use-before-declaration. A single export list with `UUID as default` generates the import first. --- index.mjs | 3 +-- types/index.d.mts | 3 +-- types/index.d.mts.map | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/index.mjs b/index.mjs index 4c8f455..e3a333f 100644 --- a/index.mjs +++ b/index.mjs @@ -33,5 +33,4 @@ */ import { UUID, ISSUER_CATEGORIES } from "@cldmv/uuid/main"; -export { UUID, UUID as uuid, ISSUER_CATEGORIES }; -export default UUID; +export { UUID as default, UUID, UUID as uuid, ISSUER_CATEGORIES }; diff --git a/types/index.d.mts b/types/index.d.mts index 808e242..e778498 100644 --- a/types/index.d.mts +++ b/types/index.d.mts @@ -1,5 +1,4 @@ -export default UUID; import { UUID } from "@cldmv/uuid/main"; import { ISSUER_CATEGORIES } from "@cldmv/uuid/main"; -export { UUID, UUID as uuid, ISSUER_CATEGORIES }; +export { UUID as default, UUID, UUID as uuid, ISSUER_CATEGORIES }; //# sourceMappingURL=index.d.mts.map \ No newline at end of file diff --git a/types/index.d.mts.map b/types/index.d.mts.map index 842020d..fa661d6 100644 --- a/types/index.d.mts.map +++ b/types/index.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mjs"],"names":[],"mappings":";qBAiCwC,kBAAkB;kCAAlB,kBAAkB"} \ No newline at end of file +{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mjs"],"names":[],"mappings":"qBAiCwC,kBAAkB;kCAAlB,kBAAkB"} \ No newline at end of file From 608219a1959ded4fab5b0f3482a21ded1ced4d80 Mon Sep 17 00:00:00 2001 From: "cldmv-bot[bot]" <230771808+cldmv-bot[bot]@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:19:32 +0000 Subject: [PATCH 03/15] chore: bump version to 1.2.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ef5bc4..0544377 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cldmv/uuid", - "version": "1.2.4", + "version": "1.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cldmv/uuid", - "version": "1.2.4", + "version": "1.2.5", "license": "Apache-2.0", "devDependencies": { "@cldmv/configs": "^1.2.1", diff --git a/package.json b/package.json index 4a021c6..df46453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cldmv/uuid", - "version": "1.2.4", + "version": "1.2.5", "description": "Extended RFC 4122 and RFC 9562 UUID implementation with custom variant structures, issuer-based identification, and timestamp variants", "main": "./index.cjs", "module": "./index.mjs", From 8fac76acda0a130871d8375ca29729159d3290ce Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 16:46:55 -0700 Subject: [PATCH 04/15] docs(changelog): backfill v1.2.4 and correct the v1.2.3 release month --- docs/changelog/v1/v1.2.3.md | 2 +- docs/changelog/v1/v1.2.4.md | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/v1/v1.2.4.md diff --git a/docs/changelog/v1/v1.2.3.md b/docs/changelog/v1/v1.2.3.md index 6f078c2..13292ec 100644 --- a/docs/changelog/v1/v1.2.3.md +++ b/docs/changelog/v1/v1.2.3.md @@ -1,6 +1,6 @@ # @cldmv/uuid v1.2.3 Changelog -**Release Date**: September 2026 +**Release Date**: October 2026 **Release Type**: Patch **Branch**: `release/1.2.3` diff --git a/docs/changelog/v1/v1.2.4.md b/docs/changelog/v1/v1.2.4.md new file mode 100644 index 0000000..e3f63ca --- /dev/null +++ b/docs/changelog/v1/v1.2.4.md @@ -0,0 +1,41 @@ +# @cldmv/uuid v1.2.4 Changelog + +**Release Date**: October 2026 +**Release Type**: Patch + +--- + +## Overview + +v1.2.4 changes only development tooling and CI. **No runtime code changed.** The shipped `index.mjs`, `index.cjs`, `dist/` and `types/` files differ from v1.2.3 only in their file-header comments (and the regenerated type source maps that follow from them), so UUID generation, parsing and the public API behave exactly as before. + +The release moves header maintenance onto the shared CLDMV `@cldmv/fix-headers` configuration, makes the required PR check report a real result on in-repo PRs, and bumps the test runner. + +--- + +## ๐Ÿ”ง CI & tooling + +### Shared fix-headers configuration ([#46](https://github.com/CLDMV/uuid/pull/46)) + +`npm run fix:headers` now runs the `fix-headers` CLI directly against a checked-in `.configs/fix-headers.json`, which extends `@cldmv/configs/fix-headers.json`. The local `tools/fix-headers.mjs` wrapper and its `tools/lib/header-config.mjs` folder list are gone. Running the shared config once rewrote the file headers across the repository into the uniform CLDMV format, which is why almost every file shows a header-only change in this release. + +### Run the in-repo PR mirror job instead of skipping it ([#47](https://github.com/CLDMV/uuid/pull/47)) + +On a PR opened from a branch in this repository, the push run reports `โœ… Required PR Check` for the commit, so the `pull_request` run's copy of the job used to be skipped. A skipped job counts as passing for a required check, which could let a PR look mergeable before its tests finished. The mirror job in `ci.yml` now always runs and exits early with a note when the push run owns the status, so it never reports as skipped. + +## ๐Ÿ”ง Dependencies + +All dev-only; the package still has no runtime dependencies. + +- `@cldmv/fix-headers` 1.3.12 โ†’ 2.1.2, plus `@cldmv/configs` 1.2.1 added for the shared config (dev; [#46](https://github.com/CLDMV/uuid/pull/46)) +- `@cldmv/vitest-runner` 1.2.0 โ†’ 1.5.1 (dev; [#44](https://github.com/CLDMV/uuid/pull/44)) + +## ๐Ÿ“š Documentation + +- **NEW:** [docs/changelog/v1/v1.2.4.md](./v1.2.4.md): this changelog, added after the release. + +--- + +## Upgrade notes + +- No breaking changes. This is a drop-in replacement for v1.2.3, and no runtime code changed. From 50e3d6fd769bdb62641474734288c9bd3b774f9d Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 16:49:02 -0700 Subject: [PATCH 05/15] docs: add v1.2.5 release notes and restructure the README --- README.md | 192 ++++++++++++++++++++++++------------ docs/changelog/v1/v1.2.5.md | 43 ++++++++ 2 files changed, 173 insertions(+), 62 deletions(-) create mode 100644 docs/changelog/v1/v1.2.5.md diff --git a/README.md b/README.md index 7a0f582..e973d0d 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,50 @@ # @cldmv/uuid -Extended UUID specification designed for RFC inclusion, formally extending RFC 4122/9562 with custom variant structures for issuer-based identification and enhanced timestamp variants. +**@cldmv/uuid** is an extended UUID specification designed for RFC inclusion. It formally extends RFC 4122/9562 with custom variant structures for issuer-based identification and enhanced timestamp variants, and ships a complete implementation of the standard RFC UUID versions alongside it. -[![npm version]][npm_version_url] [![npm downloads]][npm_downloads_url] [![GitHub downloads]][github_downloads_url] [![Last commit]][last_commit_url] [![npm last update]][npm_last_update_url] [![Coverage]][coverage_url] +The custom variants (`TA`, `TB`, `IA`) live in the variant `111` namespace, so they never collide with standard RFC UUIDs, and the same `UUID` class parses, validates and inspects both. The package has no runtime dependencies and runs in Node.js and in browser bundles. + +> _RFC-ready custom UUID variants, with every standard RFC UUID version included._ + +[![npm version]][npm_version_url] [![npm downloads]][npm_downloads_url] [![GitHub downloads]][github_downloads_url] [![Last commit]][last_commit_url] [![npm last update]][npm_last_update_url] [![coverage]][coverage_url] [![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url] +--- + ## โœจ What's New -### Latest: v1.2.3 (September 2026) +### Latest: v1.2.5 (October 2026) -- **Release tooling only, no runtime change**: the CI and release workflows now match the `CLDMV/.github` v4.29.2 templates. That adds an approval-gated release merge that keeps the curated release notes, SLSA build provenance for published releases, auto-merge for member PRs and automatic recovery for stuck Dependabot PRs ([#37](https://github.com/CLDMV/uuid/pull/37)). A new bundle-size check tracks the published `index.mjs` / `index.cjs` / `dist/` files ([#38](https://github.com/CLDMV/uuid/pull/38)). The shipped code is the same as in v1.2.2. -- [View full v1.2.3 Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.3.md) +- **`require()` works**: the CommonJS entry failed to load in every earlier release, because the ESM entry it wraps used top-level `await`, which Node's synchronous `require(esm)` rejects with `ERR_REQUIRE_ASYNC_MODULE`. The entry no longer uses top-level `await`, so `require("@cldmv/uuid")` now returns the same `UUID` object as `import` on Node.js ^20.19.0 or >=22.12.0, and older versions get a clear error that points to `import()`. ESM behavior and the exported names are unchanged ([#50](https://github.com/CLDMV/uuid/pull/50)). +- [View full v1.2.5 Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.5.md) ### Recent Releases +- **v1.2.4** (October 2026): dev tooling only, moves header maintenance to the shared CLDMV fix-headers config and stops the in-repo PR mirror check from reporting as skipped, no runtime change ([#46](https://github.com/CLDMV/uuid/pull/46), [#47](https://github.com/CLDMV/uuid/pull/47)) ([Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.4.md)) +- **v1.2.3** (October 2026): release tooling only, syncs the workflows with the `CLDMV/.github` v4.29.2 templates and adds a bundle-size check, no runtime change ([#37](https://github.com/CLDMV/uuid/pull/37), [#38](https://github.com/CLDMV/uuid/pull/38)) ([Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.3.md)) - **v1.2.2** (September 2026): dev-only bump of `@cldmv/fix-headers` from 1.3.9 to 1.3.11, no runtime change ([#31](https://github.com/CLDMV/uuid/pull/31)) ([Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.2.md)) - **v1.2.1** (September 2026): CI only, passes `BOT_NAME` / `BOT_EMAIL` to the v4 release and feature-PR workflows, no runtime change ([#27](https://github.com/CLDMV/uuid/pull/27)) ([Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.1.md)) -- **v1.2.0** (August 2026): UUID generation no longer imports any Node built-ins, so browser bundlers can use it without polyfills ([#23](https://github.com/CLDMV/uuid/pull/23)) ([Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.0.md)) -- **v1.1.7** (August 2026): exposes `./package.json` in the `exports` map ([#18](https://github.com/CLDMV/uuid/pull/18)) ([Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.1.7.md)) ๐Ÿ“š **For complete version history and detailed release notes, see the [docs/changelog/](https://github.com/CLDMV/uuid/tree/master/docs/changelog/) folder.** --- -## Overview +## ๐Ÿš€ Key Features + +- ๐Ÿ†• **RFC-Ready Specification**: Extended variant (111) with formal bit layout and entropy analysis +- ๐Ÿ”ง **Issuer Variant**: 10-bit ID space (0-1023) with categorized allocation (Technology, Open Source, Reserved) +- โฑ๏ธ **Timestamp Variants**: Signed 70-bit timestamps (TA=seconds, TB=milliseconds) with negative timestamp support +- ๐ŸŽฏ **Type-Safe**: ESM-first with complete TypeScript definitions +- โšก **High Performance**: Optimized bit manipulation, 90K+ UUIDs/sec +- ๐Ÿ”’ **Collision-Resistant**: Cryptographic entropy sources with validation +- ๐Ÿ“ฆ **Zero Dependencies**: No external runtime dependencies +- ๐Ÿงช **Thoroughly Tested**: 170+ tests covering all specification requirements +- โœ… **Bonus: RFC Support**: Complete v1/v3/v4/v5/v6/v7 implementation included + +--- + +## ๐Ÿ“– Specification Overview This library implements a **new UUID specification** that formally extends the RFC 4122/9562 namespace with: @@ -36,25 +56,24 @@ This library implements a **new UUID specification** that formally extends the R The specification is designed for formal RFC submission and includes comprehensive implementation details, entropy requirements, and collision resistance analysis. -## Features +--- -- ๐Ÿ†• **RFC-Ready Specification**: Extended variant (111) with formal bit layout and entropy analysis -- ๐Ÿ”ง **Issuer Variant**: 10-bit ID space (0-1023) with categorized allocation (Technology, Open Source, Reserved) -- โฑ๏ธ **Timestamp Variants**: Signed 70-bit timestamps (TA=seconds, TB=milliseconds) with negative timestamp support -- ๐ŸŽฏ **Type-Safe**: ESM-first with complete TypeScript definitions -- โšก **High Performance**: Optimized bit manipulation, 90K+ UUIDs/sec -- ๐Ÿ”’ **Collision-Resistant**: Cryptographic entropy sources with validation -- ๐Ÿ“ฆ **Zero Dependencies**: No external runtime dependencies -- ๐Ÿงช **Thoroughly Tested**: 170+ tests covering all specification requirements -- โœ… **Bonus: RFC Support**: Complete v1/v3/v4/v5/v6/v7 implementation included +## ๐Ÿ“ฆ Installation + +### Requirements + +- **ESM (`import`)**: Node.js v16.12.0 or higher (the package's `engines.node` floor), or any modern browser bundler through the `browser` export condition. +- **CommonJS (`require()`)**: Node.js ^20.19.0 or >=22.12.0. `index.cjs` loads the ESM entry through Node's synchronous `require(esm)`, which older versions don't have; on those, load the package with `import()` instead. -## Installation +### Install ```bash npm install @cldmv/uuid ``` -## Quick Start +--- + +## ๐Ÿš€ Quick Start ### Custom UUID Variants (RFC Specification) @@ -127,7 +146,16 @@ if (UUID.validateRFC(v4)) { } ``` -## Default String Representation +CommonJS works the same way on Node.js ^20.19.0 or >=22.12.0: + +```javascript +const UUID = require("@cldmv/uuid"); // also UUID.UUID, UUID.uuid, UUID.ISSUER_CATEGORIES +const id = UUID.TB(); +``` + +--- + +## ๐Ÿ”ค Default String Representation UUIDs automatically convert to strings when used in string contexts. This provides a seamless developer experience: @@ -155,7 +183,9 @@ const buffer = uuid.toBuffer(); // 2 (milliseconds) ### Standard RFC UUID Examples -### Standard RFC UUID Examples - ```javascript import { UUID } from "@cldmv/uuid"; @@ -895,7 +927,9 @@ if (UUID.validateRFC(v4)) { } ``` -## Performance +--- + +## โšก Performance The library is optimized for high-performance UUID generation with collision resistance: @@ -907,7 +941,9 @@ The library is optimized for high-performance UUID generation with collision res - **Cryptographically Secure**: Uses Node.js crypto.randomBytes() for entropy - **Proper Entropy Validation**: All generated UUIDs validated for entropy quality -## Demonstration Script +--- + +## ๐ŸŽฌ Demonstration Script See the custom UUID specification in action with a comprehensive human-readable demonstration: @@ -948,14 +984,17 @@ Timestamp Information: ISO 8601 : 2025-12-20T03:57:34.000Z ``` -## Development & Testing +--- + +## ๐Ÿงช Development & Testing ### Running Tests ```bash -npm test # Run all tests -npm run test:watch # Watch mode -npm run test:coverage # With coverage +npm test # Run all tests (Vitest suites, then the CommonJS entry tests) +npm run test:watch # Watch mode +npm run test:cjs # CommonJS entry tests only (Node's built-in test runner) +npm run coverage # With coverage ``` ### Test Coverage @@ -974,7 +1013,9 @@ npm run test:coverage # With coverage All tests pass with 100% specification compliance. -## TypeScript Support +--- + +## ๐Ÿ”ท TypeScript Support Full TypeScript definitions included for both custom and RFC UUID APIs: @@ -1001,9 +1042,13 @@ const bytes: Uint8Array = UUID.parse(v4); const rfcVersion: number | null = UUID.version(v4); ``` -## Specification Documentation +--- + +## ๐Ÿ“š Documentation -The complete formal specification is available in [uuid-spec.md](uuid-spec.md), including: +### Specification + +The complete formal specification is available in [uuid-spec.md](https://github.com/CLDMV/uuid/blob/master/uuid-spec.md), including: - Detailed bit layout diagrams - Entropy requirement calculations (Birthday Bound analysis) @@ -1013,42 +1058,54 @@ The complete formal specification is available in [uuid-spec.md](uuid-spec.md), - Collision resistance proofs - RFC submission rationale -## License +### Changelog -Apache-2.0 ยฉ [CLDMV](https://github.com/CLDMV) +- **[Changelog](https://github.com/CLDMV/uuid/tree/master/docs/changelog/)**: per-version release notes for every release since v1.0.0 -This specification and implementation are provided for RFC standardization consideration. +### Related Projects & Standards -## Contributing +- **[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)** - Original UUID specification +- **[RFC 9562](https://datatracker.ietf.org/doc/html/rfc9562)** - Updated UUID specification with v6, v7, v8 +- **[uuid](https://www.npmjs.com/package/uuid)** - Standard RFC 4122 UUID implementation (Node.js) +- **[ulid](https://www.npmjs.com/package/ulid)** - Universally Unique Lexicographically Sortable Identifier + +This specification extends the RFC namespace with custom variant 111, maintaining full compatibility with existing RFC 4122/9562 UUIDs. + +[![CodeFactor]][codefactor_url] [![OpenSSF Scorecard]][ossf_scorecard_url] [![npms.io score]][npms_url] [![npm unpacked size]][npm_size_url] [![Repo size]][repo_size_url] + +--- + +## ๐Ÿค Contributing Contributions to the specification and implementation are welcome! This project aims for RFC standardization, so contributions should maintain: - **Specification Compliance**: All changes must align with the formal specification - **Backward Compatibility**: Immutable fields (variant, subvariant positions) cannot change - **Comprehensive Testing**: New features require corresponding test coverage -- **Documentation**: Changes to the specification must update [uuid-spec.md](uuid-spec.md) +- **Documentation**: Changes to the specification must update [uuid-spec.md](https://github.com/CLDMV/uuid/blob/master/uuid-spec.md) -Please read the contributing guidelines before submitting pull requests. +[![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url] -## Support & Discussion +--- -- ๐Ÿ› [Report Issues](https://github.com/CLDMV/uuid/issues) -- ๐Ÿ’ฌ [Specification Discussions](https://github.com/CLDMV/uuid/discussions) -- ๐Ÿ“– [Full Specification Document](uuid-spec.md) -- ๐Ÿ’ฐ [Sponsor Development](https://github.com/sponsors/shinrai) +## ๐Ÿ”— Links -## Related Projects & Standards +- **npm**: [@cldmv/uuid](https://www.npmjs.com/package/@cldmv/uuid) +- **GitHub**: [CLDMV/uuid](https://github.com/CLDMV/uuid) +- **Issues**: [GitHub Issues](https://github.com/CLDMV/uuid/issues) +- **Specification**: [uuid-spec.md](https://github.com/CLDMV/uuid/blob/master/uuid-spec.md) +- **Changelog**: [docs/changelog/](https://github.com/CLDMV/uuid/tree/master/docs/changelog/) +- **Sponsor**: [GitHub Sponsors](https://github.com/sponsors/shinrai) -- **[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)** - Original UUID specification -- **[RFC 9562](https://datatracker.ietf.org/doc/html/rfc9562)** - Updated UUID specification with v6, v7, v8 -- **[uuid](https://www.npmjs.com/package/uuid)** - Standard RFC 4122 UUID implementation (Node.js) -- **[ulid](https://www.npmjs.com/package/ulid)** - Universally Unique Lexicographically Sortable Identifier +--- -This specification extends the RFC namespace with custom variant 111, maintaining full compatibility with existing RFC 4122/9562 UUIDs. +## ๐Ÿ“„ License + +[![npm license]][npm_license_url] -## Changelog +Apache-2.0 ยฉ Shinrai / CLDMV -See [docs/changelog/](https://github.com/CLDMV/uuid/tree/master/docs/changelog/) for per-version release notes covering every release since v1.0.0. +This specification and implementation are provided for RFC standardization consideration. --- @@ -1056,7 +1113,6 @@ See [docs/changelog/](https://github.com/CLDMV/uuid/tree/master/docs/changelog/) Made with โค๏ธ by [CLDMV](https://cldmv.net) - @@ -1064,14 +1120,26 @@ Made with โค๏ธ by [CLDMV](https://cldmv.net) [npm version]: https://img.shields.io/npm/v/%40cldmv%2Fuuid.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 [npm_version_url]: https://www.npmjs.com/package/@cldmv/uuid -[npm downloads]: https://img.shields.io/npm/dm/%40cldmv%2Fuuid.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 -[npm_downloads_url]: https://www.npmjs.com/package/@cldmv/uuid -[github downloads]: https://img.shields.io/github/downloads/CLDMV/uuid/total?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 -[github_downloads_url]: https://github.com/CLDMV/uuid/releases [last commit]: https://img.shields.io/github/last-commit/CLDMV/uuid?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 [last_commit_url]: https://github.com/CLDMV/uuid/commits [npm last update]: https://img.shields.io/npm/last-update/%40cldmv%2Fuuid?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 [npm_last_update_url]: https://www.npmjs.com/package/@cldmv/uuid +[codefactor]: https://img.shields.io/codefactor/grade/github/CLDMV/uuid?style=for-the-badge&logo=codefactor&logoColor=white&labelColor=F44A6A +[codefactor_url]: https://www.codefactor.io/repository/github/cldmv/uuid +[openssf scorecard]: https://img.shields.io/ossf-scorecard/github.com/CLDMV/uuid?style=for-the-badge&label=OpenSSF%20Scorecard +[ossf_scorecard_url]: https://scorecard.dev/viewer/?uri=github.com/CLDMV/uuid +[npms.io score]: https://img.shields.io/npms-io/final-score/%40cldmv%2Fuuid?style=for-the-badge&logo=npms&logoColor=white&labelColor=0B5D57 +[npms_url]: https://npms.io/search?q=%40cldmv%2Fuuid +[npm downloads]: https://img.shields.io/npm/dm/%40cldmv%2Fuuid.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 +[npm_downloads_url]: https://www.npmjs.com/package/@cldmv/uuid +[github downloads]: https://img.shields.io/github/downloads/CLDMV/uuid/total?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 +[github_downloads_url]: https://github.com/CLDMV/uuid/releases +[npm unpacked size]: https://img.shields.io/npm/unpacked-size/%40cldmv%2Fuuid.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 +[npm_size_url]: https://www.npmjs.com/package/@cldmv/uuid +[repo size]: https://img.shields.io/github/repo-size/CLDMV/uuid?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 +[repo_size_url]: https://github.com/CLDMV/uuid +[npm license]: https://img.shields.io/npm/l/%40cldmv%2Fuuid.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 +[npm_license_url]: https://www.npmjs.com/package/@cldmv/uuid [coverage]: https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FCLDMV%2Fuuid%2Fbadges%2Fcoverage.json&style=for-the-badge&logo=vitest&logoColor=white [coverage_url]: https://github.com/CLDMV/uuid/blob/badges/coverage.json [contributors]: https://img.shields.io/github/contributors/CLDMV/uuid.svg?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 diff --git a/docs/changelog/v1/v1.2.5.md b/docs/changelog/v1/v1.2.5.md new file mode 100644 index 0000000..6a41b1d --- /dev/null +++ b/docs/changelog/v1/v1.2.5.md @@ -0,0 +1,43 @@ +# @cldmv/uuid v1.2.5 Changelog + +**Release Date**: October 2026 +**Release Type**: Patch +**Branch**: `release/1.2.5` + +--- + +## Overview + +v1.2.5 makes `require("@cldmv/uuid")` work. The CommonJS entry has failed to load in every release up to and including v1.2.4, because the ESM entry it wraps used top-level `await`. This release removes the top-level `await`, so `require()` returns the same `UUID` object that `import` does on any Node.js version with synchronous `require(esm)`, and fails with a clear message on versions without it. + +ESM consumers see no change in behavior. The exported names (`default`, `UUID`, `uuid`, `ISSUER_CATEGORIES`) and their values are the same as in v1.2.4. + +--- + +## ๐Ÿ› Bug Fixes + +### Load the CommonJS entry without top-level await ([#50](https://github.com/CLDMV/uuid/pull/50)) + +`index.cjs` loads `index.mjs` through Node's synchronous `require(esm)`. Node rejects any module graph that contains top-level `await` there, and `index.mjs` had two: one around the optional `devcheck.mjs` import and one for `await import("@cldmv/uuid/main")`. So on Node.js versions with `require(esm)`, `require("@cldmv/uuid")` threw `ERR_REQUIRE_ASYNC_MODULE`, and on older versions it threw `ERR_REQUIRE_ESM`. Either way, no CommonJS consumer could load the package. + +- `index.mjs` now imports `@cldmv/uuid/main` statically, and the optional development check runs inside an async function rather than at the top level. `devcheck.mjs` exists only in a source checkout and has never been published, so its import is still allowed to fail. +- `index.cjs` calls `require("./index.mjs")` directly instead of going through `createRequire`. +- On a Node.js version without `require(esm)` (anything before 20.19.0, or 22.0.0 to 22.11.x), `index.cjs` now throws an `ERR_REQUIRE_ESM` error whose message names the supported versions and points to `import()` instead. +- A new `tests/cjs/entry.test.cjs` suite runs under Node's own test runner, since Vitest loads files through its own module runner and can't show how a plain `require()` behaves. It checks that `require()` returns the same objects as `import` and that the error message appears when `require(esm)` is turned off. `npm test` and `npm run coverage` both run it through the new `test:cjs` script. + +### Export the default from the named export list ([#50](https://github.com/CLDMV/uuid/pull/50)) + +`tsc` emitted `export default UUID;` above the declaration it referred to in `types/index.d.mts`, which CodeQL flags as `js/use-before-declaration`. The entry now exports a single list (`export { UUID as default, UUID, UUID as uuid, ISSUER_CATEGORIES }`), so the generated declaration file imports `UUID` and `ISSUER_CATEGORIES` from `@cldmv/uuid/main` first and then re-exports them. The exported types are unchanged. + +## ๐Ÿ“š Documentation + +- **NEW:** [docs/changelog/v1/v1.2.5.md](./v1.2.5.md): this changelog. +- **NEW:** backfilled [v1.2.4](./v1.2.4.md), and corrected the [v1.2.3](./v1.2.3.md) release month to October 2026. +- README restructured to the standard CLDMV layout, with a new Requirements section that states the `require()` Node.js floor. + +--- + +## Upgrade notes + +- No breaking changes. ESM imports behave exactly as in v1.2.4. +- CommonJS consumers can now use `const UUID = require("@cldmv/uuid")` (with `UUID.UUID`, `UUID.uuid` and `UUID.ISSUER_CATEGORIES` as properties). This needs Node.js ^20.19.0 or >=22.12.0. On older Node.js versions, load the package with `import()`. From af17259a500dd48ca25de1f46ff29e15c600186c Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 16:58:01 -0700 Subject: [PATCH 06/15] chore: add the Apache-2.0 LICENSE file package.json declares Apache-2.0 and lists LICENSE in `files`, but the repository never had the file, so GitHub detected no license and the published package shipped without the license text. --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 9726122e74d81cb05719946e9498a20b5621bb1b Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 17:44:42 -0700 Subject: [PATCH 07/15] fix(types): give ./main and the subpaths accurate type declarations TypeScript users got `any` for UUID: ./main had no `types` condition, so types/index.d.mts could not resolve `@cldmv/uuid/main` (TS7016, hidden by skipLibCheck). ./rng, ./bytes and ./hash had no `types` either. Wiring the condition exposed declarations that did not compile or described the API wrongly, so the source JSDoc is corrected: - v1/v4/v6/v7/v8 options are optional, with typed option objects (TimeOptions, V4Options, V7Options, V8Options), and overloads that return a Uint8Array when `options.buf` is given. - TA/TB timestamp and every `entropy` argument are optional; createTimestampVariant/timestamp accept null/undefined for the timestamp. - getRegistry() imports IssuerRegistry instead of naming an undeclared type. - The UUID class no longer references Node's Buffer, so the main entry type-checks without @types/node (the package also runs in browsers). - _buffer is declared private instead of leaking Uint8Array. ./rng, ./bytes and ./hash get `types` for the Node variants and nested `browser` -> `types` for the browser variants. `npm run test:types` (run by `npm test` and `coverage`) builds, packs the package, unpacks it into a throwaway consumer and compiles fixtures with strict, nodenext and no skipLibCheck: the main entry, ./main, the README TypeScript example, the subpaths (Node and browser conditions), and a deliberately wrong assignment that must fail. The README example is corrected to match the real types. Fixes #51 --- README.md | 8 +- package-lock.json | 18 ++ package.json | 25 ++- src/lib/versions/rfc/utils.mjs | 2 +- src/lib/versions/rfc/v1.mjs | 14 +- src/lib/versions/rfc/v35.mjs | 12 +- src/lib/versions/rfc/v4.mjs | 8 +- src/lib/versions/rfc/v6.mjs | 14 +- src/lib/versions/rfc/v7.mjs | 8 +- src/lib/versions/rfc/v8.mjs | 8 +- src/uuid.mjs | 138 ++++++++++-- tests/types/consumer.test.mjs | 234 +++++++++++++++++++++ types/src/lib/versions/rfc/utils.d.mts | 4 +- types/src/lib/versions/rfc/utils.d.mts.map | 2 +- types/src/lib/versions/rfc/v1.d.mts | 26 +-- types/src/lib/versions/rfc/v1.d.mts.map | 2 +- types/src/lib/versions/rfc/v35.d.mts | 12 +- types/src/lib/versions/rfc/v35.d.mts.map | 2 +- types/src/lib/versions/rfc/v4.d.mts | 14 +- types/src/lib/versions/rfc/v4.d.mts.map | 2 +- types/src/lib/versions/rfc/v6.d.mts | 26 +-- types/src/lib/versions/rfc/v6.d.mts.map | 2 +- types/src/lib/versions/rfc/v7.d.mts | 14 +- types/src/lib/versions/rfc/v7.d.mts.map | 2 +- types/src/lib/versions/rfc/v8.d.mts | 14 +- types/src/lib/versions/rfc/v8.d.mts.map | 2 +- types/src/uuid.d.mts | 211 +++++++++++++++---- types/src/uuid.d.mts.map | 2 +- 28 files changed, 659 insertions(+), 167 deletions(-) create mode 100644 tests/types/consumer.test.mjs diff --git a/README.md b/README.md index 7a0f582..5ad854d 100644 --- a/README.md +++ b/README.md @@ -982,9 +982,9 @@ Full TypeScript definitions included for both custom and RFC UUID APIs: import { UUID } from "@cldmv/uuid"; // Custom UUID specification (RFC-ready) -const ta: string = UUID.TA(); -const tb: string = UUID.TB(); -const ia: string = UUID.IA(404); +const ta: string = UUID.TA().toString(); +const tb: string = UUID.TB().toString(); +const ia: string = UUID.IA(404).toString(); // Instance methods with proper types const uuid = new UUID(ta); @@ -998,7 +998,7 @@ const category: string | null = uuid.getIssuerCategory(); // Standard RFC UUIDs const v4: string = UUID.v4(); const bytes: Uint8Array = UUID.parse(v4); -const rfcVersion: number | null = UUID.version(v4); +const rfcVersion: string | number | null = UUID.version(v4); // 4 here; "TA" / "TB" / "IA" for the custom variants ``` ## Specification Documentation diff --git a/package-lock.json b/package-lock.json index 0544377..598c76b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@cldmv/configs": "^1.2.1", "@cldmv/fix-headers": "^2.1.2", "@cldmv/vitest-runner": "^1.2.0", + "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.0", "typescript": "^5.9.3", "vitest": "^5.0.0" @@ -494,6 +495,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "26.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz", + "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, "node_modules/@vitest/coverage-v8": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.2.tgz", @@ -1223,6 +1234,13 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.1.tgz", diff --git a/package.json b/package.json index df46453..63d3db1 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "require": "./index.cjs" }, "./main": { + "types": "./types/dist/uuid.d.mts", "uuid-dev": { "import": "./src/uuid.mjs" }, @@ -22,7 +23,11 @@ "browser": "./src/lib/rng-browser.mjs", "import": "./src/lib/rng.mjs" }, - "browser": "./dist/lib/rng-browser.mjs", + "browser": { + "types": "./types/dist/lib/rng-browser.d.mts", + "default": "./dist/lib/rng-browser.mjs" + }, + "types": "./types/dist/lib/rng.d.mts", "import": "./dist/lib/rng.mjs" }, "./bytes": { @@ -30,7 +35,11 @@ "browser": "./src/lib/bytes-browser.mjs", "import": "./src/lib/bytes.mjs" }, - "browser": "./dist/lib/bytes-browser.mjs", + "browser": { + "types": "./types/dist/lib/bytes-browser.d.mts", + "default": "./dist/lib/bytes-browser.mjs" + }, + "types": "./types/dist/lib/bytes.d.mts", "import": "./dist/lib/bytes.mjs" }, "./hash": { @@ -38,7 +47,11 @@ "browser": "./src/lib/hash-browser.mjs", "import": "./src/lib/hash.mjs" }, - "browser": "./dist/lib/hash-browser.mjs", + "browser": { + "types": "./types/dist/lib/hash-browser.d.mts", + "default": "./dist/lib/hash-browser.mjs" + }, + "types": "./types/dist/lib/hash.d.mts", "import": "./dist/lib/hash.mjs" }, "./package.json": "./package.json" @@ -53,10 +66,11 @@ "build:dist": "node -e \"const fs = require('fs'); const path = require('path'); fs.rmSync('dist', {recursive: true, force: true}); fs.mkdirSync('dist', {recursive: true}); fs.cpSync('src', 'dist', {recursive: true});\"", "build:types": "node -e \"require('fs').rmSync('types', {recursive: true, force: true})\" && tsc --project .configs/tsconfig.dts.jsonc", "demo": "node scripts/demo-custom-uuids.mjs", - "test": "node tests/run-vitest.mjs && npm run test:cjs", + "test": "node tests/run-vitest.mjs && npm run test:cjs && npm run test:types", "test:cjs": "node --conditions=uuid-dev --test tests/cjs/entry.test.cjs", + "test:types": "npm run build && node --test tests/types/consumer.test.mjs", "test:watch": "vitest --config .configs/vitest.config.mjs", - "coverage": "node tests/run-vitest.mjs --coverage-quiet && npm run test:cjs", + "coverage": "node tests/run-vitest.mjs --coverage-quiet && npm run test:cjs && npm run test:types", "ci:coverage": "npm run coverage", "fix:headers": "fix-headers --config .configs/fix-headers.json", "types:build": "tsc -p .configs/tsconfig.dts.jsonc --noCheck", @@ -119,6 +133,7 @@ "@cldmv/configs": "^1.2.1", "@cldmv/fix-headers": "^2.1.2", "@cldmv/vitest-runner": "^1.2.0", + "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.0", "typescript": "^5.9.3", "vitest": "^5.0.0" diff --git a/src/lib/versions/rfc/utils.mjs b/src/lib/versions/rfc/utils.mjs index 0f90ef3..78abeb0 100644 --- a/src/lib/versions/rfc/utils.mjs +++ b/src/lib/versions/rfc/utils.mjs @@ -55,7 +55,7 @@ export function parse(uuid) { /** * Convert array of bytes to UUID string - * @param {Uint8Array|Buffer|Array} bytes - 16-byte array + * @param {ArrayLike} bytes - 16-byte array * @returns {string} UUID string with dashes * @example * stringify([110, 192, 189, 127, 17, 192, 67, 218, 151, 94, 42, 138, 217, 235, 174, 11]); diff --git a/src/lib/versions/rfc/v1.mjs b/src/lib/versions/rfc/v1.mjs index 8b3abb0..084927d 100644 --- a/src/lib/versions/rfc/v1.mjs +++ b/src/lib/versions/rfc/v1.mjs @@ -24,13 +24,13 @@ import { stringify } from "./utils.mjs"; /** * Create a version 1 (timestamp) UUID - * @param {Object} options - Optional parameters - * @param {Array} options.node - 6-byte node id (MAC address) - * @param {number} options.clockseq - 14-bit clock sequence - * @param {number} options.msecs - Timestamp in milliseconds since unix epoch - * @param {number} options.nsecs - Additional 100-nanosecond intervals - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {ArrayLike} [options.node] - 6-byte node id (MAC address) + * @param {number} [options.clockseq] - 14-bit clock sequence + * @param {number} [options.msecs] - Timestamp in milliseconds since unix epoch + * @param {number} [options.nsecs] - Additional 100-nanosecond intervals + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v1(options = {}) { diff --git a/src/lib/versions/rfc/v35.mjs b/src/lib/versions/rfc/v35.mjs index 4a7683a..1cb7851 100644 --- a/src/lib/versions/rfc/v35.mjs +++ b/src/lib/versions/rfc/v35.mjs @@ -29,8 +29,8 @@ import { parse, stringify } from "./utils.mjs"; * @param {string|Uint8Array} namespace - Namespace UUID * @param {number} versionByte - Version byte (0x30 for v3, 0x50 for v5) * @param {Function} hashFn - Isomorphic hash function (md5 or sha1), signature (namespaceBytes, name) - * @param {Uint8Array} buf - Optional buffer to write into - * @param {number} offset - Optional offset in buffer + * @param {Uint8Array} [buf] - Optional buffer to write into; when given, it is returned instead of a string + * @param {number} [offset] - Optional offset in buffer * @returns {string|Uint8Array} UUID string or buffer * @private */ @@ -67,8 +67,8 @@ function _v35(name, namespace, versionByte, hashFn, buf, offset) { * Create a version 3 (namespace with MD5) UUID * @param {string} name - Name to hash * @param {string|Uint8Array} namespace - Namespace UUID - * @param {Uint8Array} buf - Optional buffer to write into - * @param {number} offset - Optional offset in buffer + * @param {Uint8Array} [buf] - Optional buffer to write into; when given, it is returned instead of a string + * @param {number} [offset] - Optional offset in buffer * @returns {string|Uint8Array} UUID string or buffer */ export function v3(name, namespace, buf, offset) { @@ -79,8 +79,8 @@ export function v3(name, namespace, buf, offset) { * Create a version 5 (namespace with SHA-1) UUID * @param {string} name - Name to hash * @param {string|Uint8Array} namespace - Namespace UUID - * @param {Uint8Array} buf - Optional buffer to write into - * @param {number} offset - Optional offset in buffer + * @param {Uint8Array} [buf] - Optional buffer to write into; when given, it is returned instead of a string + * @param {number} [offset] - Optional offset in buffer * @returns {string|Uint8Array} UUID string or buffer */ export function v5(name, namespace, buf, offset) { diff --git a/src/lib/versions/rfc/v4.mjs b/src/lib/versions/rfc/v4.mjs index 1091cfa..586de69 100644 --- a/src/lib/versions/rfc/v4.mjs +++ b/src/lib/versions/rfc/v4.mjs @@ -24,10 +24,10 @@ import { stringify } from "./utils.mjs"; /** * Create a version 4 (random) UUID - * @param {Object} options - Optional parameters - * @param {Uint8Array} options.random - 16 random bytes - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {Uint8Array} [options.random] - 16 random bytes + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v4(options = {}) { diff --git a/src/lib/versions/rfc/v6.mjs b/src/lib/versions/rfc/v6.mjs index 0b807f9..0a958ff 100644 --- a/src/lib/versions/rfc/v6.mjs +++ b/src/lib/versions/rfc/v6.mjs @@ -25,13 +25,13 @@ import { stringify } from "./utils.mjs"; /** * Create a version 6 (timestamp, reordered) UUID - * @param {Object} options - Optional parameters - * @param {Array} options.node - 6-byte node id (MAC address) - * @param {number} options.clockseq - 14-bit clock sequence - * @param {number} options.msecs - Timestamp in milliseconds since unix epoch - * @param {number} options.nsecs - Additional 100-nanosecond intervals - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {ArrayLike} [options.node] - 6-byte node id (MAC address) + * @param {number} [options.clockseq] - 14-bit clock sequence + * @param {number} [options.msecs] - Timestamp in milliseconds since unix epoch + * @param {number} [options.nsecs] - Additional 100-nanosecond intervals + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v6(options = {}) { diff --git a/src/lib/versions/rfc/v7.mjs b/src/lib/versions/rfc/v7.mjs index ae1276a..89952f6 100644 --- a/src/lib/versions/rfc/v7.mjs +++ b/src/lib/versions/rfc/v7.mjs @@ -25,10 +25,10 @@ import { stringify } from "./utils.mjs"; /** * Create a version 7 (Unix Epoch time-based) UUID - * @param {Object} options - Optional parameters - * @param {number} options.msecs - Timestamp in milliseconds since unix epoch - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {number} [options.msecs] - Timestamp in milliseconds since unix epoch + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v7(options = {}) { diff --git a/src/lib/versions/rfc/v8.mjs b/src/lib/versions/rfc/v8.mjs index ac648a1..c9c1102 100644 --- a/src/lib/versions/rfc/v8.mjs +++ b/src/lib/versions/rfc/v8.mjs @@ -26,10 +26,10 @@ import { stringify } from "./utils.mjs"; /** * Create a version 8 (custom/experimental) UUID - * @param {Object} options - Optional parameters - * @param {Uint8Array} options.data - Custom data to fill the UUID (16 bytes) - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {Uint8Array} [options.data] - Custom data to fill the UUID (16 bytes) + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer * @example * // Generate with random data diff --git a/src/uuid.mjs b/src/uuid.mjs index b3260a3..ad75246 100644 --- a/src/uuid.mjs +++ b/src/uuid.mjs @@ -44,6 +44,35 @@ import { } from "./lib/constants.mjs"; import * as rfcUuids from "./lib/versions/rfc/index.mjs"; +/** + * Options shared by the RFC generators that can write into a caller-supplied buffer. + * @typedef {object} RFCBufferOptions + * @property {Uint8Array} [buf] - Buffer to write the UUID into; when given, the generator returns it instead of a string + * @property {number} [offset] - Offset in `buf` to start writing at (default 0) + */ + +/** + * Options for {@link UUID.v1} and {@link UUID.v6}. `node` is the 6-byte node id (MAC address), + * `clockseq` the 14-bit clock sequence, `msecs` the timestamp in milliseconds since the Unix + * epoch, and `nsecs` additional 100-nanosecond intervals. + * @typedef {RFCBufferOptions & { node?: ArrayLike, clockseq?: number, msecs?: number, nsecs?: number }} TimeOptions + */ + +/** + * Options for {@link UUID.v4}. `random` supplies the 16 random bytes instead of generating them. + * @typedef {RFCBufferOptions & { random?: Uint8Array }} V4Options + */ + +/** + * Options for {@link UUID.v7}. `msecs` is the timestamp in milliseconds since the Unix epoch. + * @typedef {RFCBufferOptions & { msecs?: number }} V7Options + */ + +/** + * Options for {@link UUID.v8}. `data` supplies the 16 bytes of custom data instead of random bytes. + * @typedef {RFCBufferOptions & { data?: Uint8Array }} V8Options + */ + /** * UUID class implementing the new specification */ @@ -53,6 +82,11 @@ class UUID { * @param {Uint8Array|string|null} data - Optional UUID data to parse */ constructor(data = null) { + /** + * The 16 UUID bytes. + * @type {Uint8Array} + * @private + */ this._buffer = new Uint8Array(16); if (data !== null && data !== undefined) { @@ -87,7 +121,7 @@ class UUID { * Create a new Issuer Variant UUID * @param {number} issuerID - Issuer ID (0-ISSUER_ID_MASK) * @param {number} version - Version number - * @param {Uint8Array} entropy - Additional entropy data + * @param {Uint8Array|null} [entropy] - Additional entropy data * @returns {UUID} New UUID instance */ static createIssuerVariant(issuerID, version, entropy = null) { @@ -121,9 +155,9 @@ class UUID { /** * Create a new Timestamp Variant UUID - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) + * @param {number|Date|null|undefined} timestamp - Timestamp value (null/undefined defaults to the current time) * @param {number} version - Version number - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ static createTimestampVariant(timestamp, version, entropy = null) { @@ -192,7 +226,7 @@ class UUID { * Create an issuer-based UUID (short name alias) * @param {number} issuerID - Issuer ID (0-1023) * @param {number} version - Version number - * @param {Uint8Array} entropy - Additional entropy data + * @param {Uint8Array|null} [entropy] - Additional entropy data * @returns {UUID} New UUID instance */ static issuer(issuerID, version, entropy = null) { @@ -201,9 +235,9 @@ class UUID { /** * Create a timestamp-based UUID (short name alias) - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) + * @param {number|Date|null|undefined} timestamp - Timestamp value (null/undefined defaults to the current time) * @param {number} version - Version number - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ static timestamp(timestamp, version, entropy = null) { @@ -213,8 +247,8 @@ class UUID { /** * Create Timestamp Variant v1 UUID (ultra-short alias) * Subvariant 00 - Timestamp-based identification (seconds precision) - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {number|Date|null} [timestamp] - Timestamp value (optional, defaults to the current time) + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ static TA(timestamp, entropy = null) { @@ -225,7 +259,7 @@ class UUID { * Create Issuer Variant v1 UUID (ultra-short alias) * Subvariant 01 - Issuer-based identification * @param {number} issuerID - Issuer ID (0-1023) - * @param {Uint8Array} entropy - Additional entropy data + * @param {Uint8Array|null} [entropy] - Additional entropy data * @returns {UUID} New UUID instance */ static IA(issuerID, entropy = null) { @@ -235,8 +269,8 @@ class UUID { /** * Create Timestamp Variant v2 UUID (ultra-short alias) * Subvariant 00 - Timestamp-based identification (milliseconds precision) - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {number|Date|null} [timestamp] - Timestamp value (optional, defaults to the current time) + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ static TB(timestamp, entropy = null) { @@ -286,7 +320,7 @@ class UUID { /** * Fill remaining bits with entropy while preserving immutable fields - * @param {Uint8Array} entropy - Entropy data + * @param {Uint8Array|null} [entropy] - Entropy data * @private */ _fillEntropy(entropy) { @@ -532,7 +566,7 @@ class UUID { /** * Convert UUID to buffer - * @returns {Buffer} UUID as 16-byte buffer (Node); a Uint8Array copy in environments without Buffer + * @returns {Uint8Array} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer */ toBuffer() { return toBufferLike(this._buffer); @@ -605,7 +639,7 @@ class UUID { /** * Get the shared issuer registry instance - * @returns {Promise} Shared registry instance + * @returns {Promise} Shared registry instance */ static async getRegistry() { if (!UUID._registryInstance) { @@ -674,9 +708,21 @@ class UUID { /** * Generate a version 1 (timestamp) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {TimeOptions & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ + /** + * Generate a version 1 (timestamp) UUID, written into `options.buf` + * @overload + * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + /** + * Generate a version 1 (timestamp) UUID + * @param {TimeOptions} [options] - Optional parameters + * @returns {string|Uint8Array} UUID string, or `options.buf` when one is given + */ static v1(options) { return rfcUuids.v1(options); } @@ -693,9 +739,21 @@ class UUID { /** * Generate a version 4 (random) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {V4Options & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ + /** + * Generate a version 4 (random) UUID, written into `options.buf` + * @overload + * @param {V4Options & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + /** + * Generate a version 4 (random) UUID + * @param {V4Options} [options] - Optional parameters + * @returns {string|Uint8Array} UUID string, or `options.buf` when one is given + */ static v4(options) { return rfcUuids.v4(options); } @@ -712,27 +770,63 @@ class UUID { /** * Generate a version 6 (timestamp, reordered) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {TimeOptions & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ + /** + * Generate a version 6 (timestamp, reordered) UUID, written into `options.buf` + * @overload + * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + /** + * Generate a version 6 (timestamp, reordered) UUID + * @param {TimeOptions} [options] - Optional parameters + * @returns {string|Uint8Array} UUID string, or `options.buf` when one is given + */ static v6(options) { return rfcUuids.v6(options); } /** * Generate a version 7 (Unix Epoch) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {V7Options & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ + /** + * Generate a version 7 (Unix Epoch) UUID, written into `options.buf` + * @overload + * @param {V7Options & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + /** + * Generate a version 7 (Unix Epoch) UUID + * @param {V7Options} [options] - Optional parameters + * @returns {string|Uint8Array} UUID string, or `options.buf` when one is given + */ static v7(options) { return rfcUuids.v7(options); } /** * Generate a version 8 (custom/experimental) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {V8Options & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ + /** + * Generate a version 8 (custom/experimental) UUID, written into `options.buf` + * @overload + * @param {V8Options & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + /** + * Generate a version 8 (custom/experimental) UUID + * @param {V8Options} [options] - Optional parameters + * @returns {string|Uint8Array} UUID string, or `options.buf` when one is given + */ static v8(options) { return rfcUuids.v8(options); } @@ -748,7 +842,7 @@ class UUID { /** * Convert byte array to UUID string - * @param {Uint8Array|Buffer|Array} bytes - 16-byte array + * @param {ArrayLike} bytes - 16-byte array (Uint8Array, Buffer or plain array) * @returns {string} UUID string */ static stringify(bytes) { @@ -766,7 +860,7 @@ class UUID { /** * Detect version/variant identifier of UUID (handles both RFC and custom variants) - * @param {string|Buffer|UUID} uuid - UUID string, buffer, or UUID instance + * @param {string|Uint8Array|UUID} uuid - UUID string, buffer, or UUID instance * @returns {string|number|null} Version identifier (e.g., "TA", "TB", "IA" for custom, 1-8 for RFC, or null if invalid) * @example * UUID.version(uuidString); // => "TA" for Timestamp v1 @@ -795,7 +889,7 @@ class UUID { /** * Detect variant identifier (alias for version()) - * @param {string|Buffer|UUID} uuid - UUID string, buffer, or UUID instance + * @param {string|Uint8Array|UUID} uuid - UUID string, buffer, or UUID instance * @returns {string|number|null} Version identifier * @deprecated Use UUID.version() instead */ diff --git a/tests/types/consumer.test.mjs b/tests/types/consumer.test.mjs new file mode 100644 index 0000000..3224552 --- /dev/null +++ b/tests/types/consumer.test.mjs @@ -0,0 +1,234 @@ +/** + * + * @Project: @cldmv/uuid + * @Filename: /tests/types/consumer.test.mjs + * @Date: 2026-10-03T17:37:05-07:00 (1791074225) + * @Author: Nate Corcoran + * @Email: + * ----- + * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-10-03T17:44:07-07:00 (1791074647) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + * + */ + +/** + * Consumer type-check tests. These run under Node's own test runner (`node --test`), not + * Vitest: they pack the package with `npm pack`, unpack the tarball into a throwaway + * consumer project, and compile TypeScript fixtures that import it by name with + * `moduleResolution: nodenext`, `strict: true` and no `skipLibCheck`. That is what a + * TypeScript user installing the published package sees, so it catches a missing `types` + * condition (the import silently becomes `any`) as well as declarations that do not + * compile or describe the API wrongly. + * + * Needs a build first (`npm run build`): the published declarations live in types/dist/. + * `npm run test:types` builds and then runs this file. + */ +import { test, before, after } from "node:test"; +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const require = createRequire(import.meta.url); +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const tscBin = require.resolve("typescript/bin/tsc"); +// Inside the repo's gitignored tmp/ so the consumer can find @types/node in the repo's +// node_modules (TypeScript walks up the tree for type roots), while @cldmv/uuid itself +// resolves to the unpacked tarball in the consumer's own node_modules. +const workDir = path.join(repoRoot, "tmp", `types-consumer-${process.pid}`); +const consumerDir = path.join(workDir, "consumer"); + +/** + * Write a tsconfig for one fixture and compile it. + * @param {string} name - Fixture name, used for the tsconfig file name. + * @param {string[]} files - Fixture files to compile. + * @param {string[]} types - Global type packages to load (`[]` = none, not even @types/node). + * @param {object} [extra] - Additional compiler options. + * @returns {{ status: number|null, output: string }} tsc exit status and combined output. + */ +function compile(name, files, types, extra = {}) { + const tsconfig = path.join(consumerDir, `tsconfig.${name}.json`); + writeFileSync( + tsconfig, + JSON.stringify( + { + compilerOptions: { + strict: true, + module: "nodenext", + moduleResolution: "nodenext", + target: "es2022", + lib: ["es2022"], + types, + noEmit: true, + skipLibCheck: false, + ...extra + }, + files + }, + null, + "\t" + ) + ); + const res = spawnSync(process.execPath, [tscBin, "-p", tsconfig, "--pretty", "false"], { cwd: consumerDir, encoding: "utf8" }); + return { status: res.status, output: `${res.stdout}${res.stderr}` }; +} + +before(() => { + for (const built of ["dist/uuid.mjs", "types/dist/uuid.d.mts"]) { + assert.ok(existsSync(path.join(repoRoot, built)), `${built} is missing; run \`npm run build\` first (or use \`npm run test:types\`)`); + } + + rmSync(workDir, { recursive: true, force: true }); + const pkgDir = path.join(consumerDir, "node_modules", "@cldmv", "uuid"); + mkdirSync(pkgDir, { recursive: true }); + + const pack = spawnSync("npm", ["pack", "--pack-destination", workDir], { + cwd: repoRoot, + encoding: "utf8", + shell: process.platform === "win32" + }); + assert.equal(pack.status, 0, `npm pack failed:\n${pack.stderr}`); + // workDir was emptied above, so the tarball npm just wrote is the only .tgz in it. + const [filename] = readdirSync(workDir).filter((file) => file.endsWith(".tgz")); + assert.ok(filename, `npm pack wrote no tarball:\n${pack.stdout}`); + + const untar = spawnSync("tar", ["-xzf", path.join(workDir, filename), "-C", pkgDir, "--strip-components=1"], { encoding: "utf8" }); + assert.equal(untar.status, 0, `tar failed:\n${untar.stderr}`); + + writeFileSync(path.join(consumerDir, "package.json"), JSON.stringify({ name: "uuid-types-consumer", private: true, type: "module" })); + + writeFileSync( + path.join(consumerDir, "main.mts"), + `import UUIDDefault, { UUID, uuid, ISSUER_CATEGORIES } from "@cldmv/uuid"; +import { UUID as MainUUID, uuid as mainUuid, ISSUER_CATEGORIES as MAIN_CATEGORIES } from "@cldmv/uuid/main"; + +// The default export, the named exports and the ./main subpath are all the same class. +const same: typeof UUID = UUIDDefault; +const sameLower: typeof UUID = uuid; +const sameMain: typeof UUID = MainUUID; +const sameMainLower: typeof UUID = mainUuid; + +// RFC generators: options are optional, and passing a buffer returns the buffer. +const v1: string = UUID.v1(); +const v4: string = UUID.v4(); +const v4Random: string = UUID.v4({ random: new Uint8Array(16) }); +const v4Buf: Uint8Array = UUID.v4({ buf: new Uint8Array(16), offset: 0 }); +const v6: string = UUID.v6({ msecs: Date.now() }); +const v7: string = UUID.v7(); +const v8: string = UUID.v8({ data: new Uint8Array(16) }); +const v1Node: string = UUID.v1({ node: [1, 2, 3, 4, 5, 6], clockseq: 0 }); +const v3: string = UUID.v3("example.com", UUID.DNS); +const v5: string = UUID.v5("https://example.com", UUID.URL); +const nil: string = UUID.NIL; +const parsed: Uint8Array = UUID.parse(v4); +const text: string = UUID.stringify(parsed); +const fromArray: string = UUID.stringify(Array.from(parsed)); +const valid: boolean = UUID.validateRFC(v4); +const rfcVersion: string | number | null = UUID.version(v4); + +// Custom variants: the timestamp argument is optional. +const ta: UUID = UUID.TA(); +const tb: UUID = UUID.TB(new Date()); +const ia: UUID = UUID.IA(ISSUER_CATEGORIES.SPEC_ORIGINATOR); +const viaTimestamp: UUID = UUID.createTimestampVariant(undefined, 2); +const viaIssuer: UUID = UUID.createIssuerVariant(MAIN_CATEGORIES.UNASSIGNED, 1, new Uint8Array(16)); +const instance = new UUID(); +const fromString = new UUID(ta.toString()); +const ts: number | null = tb.getTimestamp(); +const issuerID: number | null = ia.getIssuerID(); +const bytes: Uint8Array = ta.toBuffer(); +const asString: string = \`\${ta}\`; +const json: string = JSON.stringify({ id: ta }); +const category: number = ISSUER_CATEGORIES.UNASSIGNED; + +// Registry and validation helpers. +const registry = await UUID.getRegistry(); +const available: boolean = registry.isAvailable(300); +const info = await UUID.getIssuerInfo(1); +const report = await UUID.validate(ta.toBuffer()); + +export { + same, sameLower, sameMain, sameMainLower, v1, v4, v4Random, v4Buf, v6, v7, v8, v1Node, v3, v5, nil, parsed, + text, fromArray, valid, rfcVersion, ta, tb, ia, viaTimestamp, viaIssuer, instance, fromString, ts, issuerID, + bytes, asString, json, category, available, info, report +}; +` + ); + + writeFileSync( + path.join(consumerDir, "subpaths.mts"), + `import { randomBytes } from "@cldmv/uuid/rng"; +import { fromHex, toHex, toBufferLike } from "@cldmv/uuid/bytes"; +import { md5, sha1 } from "@cldmv/uuid/hash"; + +const random: Uint8Array = randomBytes(16); +const hex: string = toHex(random); +const back: Uint8Array = fromHex(hex); +const copy: Uint8Array = toBufferLike(back); +const md5Digest: Uint8Array = md5(random, "name"); +const sha1Digest: Uint8Array = sha1(random, "name"); + +export { random, hex, back, copy, md5Digest, sha1Digest }; +` + ); + + // The README's TypeScript example, verbatim, so the documented types stay true. + const readme = readFileSync(path.join(repoRoot, "README.md"), "utf8"); + const example = /TypeScript Support\n[\s\S]*?```typescript\n([\s\S]*?)```/.exec(readme); + assert.ok(example, "README.md has no ```typescript block under its TypeScript Support heading"); + writeFileSync(path.join(consumerDir, "readme.mts"), `${example[1]}\nexport {};\n`); + + writeFileSync( + path.join(consumerDir, "wrong.mts"), + `import { UUID } from "@cldmv/uuid"; + +const n: number = UUID.v4(); + +export { n }; +` + ); +}); + +after(() => { + rmSync(workDir, { recursive: true, force: true }); +}); + +test("the main entry and ./main type-check without @types/node", () => { + // types: [] keeps @types/node out, so this also shows the UUID declarations do not + // depend on Node-only globals such as Buffer (the package also runs in browsers). + const { status, output } = compile("main", ["main.mts"], []); + assert.equal(status, 0, output); +}); + +test("the README TypeScript example type-checks", () => { + // noUnusedLocals is off by default, so the example's unused bindings are fine. + const { status, output } = compile("readme", ["readme.mts"], []); + assert.equal(status, 0, output); +}); + +test("the ./rng, ./bytes and ./hash subpaths type-check", () => { + // The Node variants of these modules return Buffers, so they need @types/node like any + // other Node-only declaration. + const { status, output } = compile("subpaths", ["subpaths.mts"], ["node"]); + assert.equal(status, 0, output); +}); + +test("the browser variants of ./rng, ./bytes and ./hash type-check without @types/node", () => { + // With the "browser" condition the browser declarations are picked, which return plain + // Uint8Arrays and must not need Node's globals. + const { status, output } = compile("browser", ["subpaths.mts"], [], { customConditions: ["browser"] }); + assert.equal(status, 0, output); +}); + +test("a wrong assignment from UUID.v4() fails to compile", () => { + // If UUID were `any` (no types condition on ./main), this would compile. + const { status, output } = compile("wrong", ["wrong.mts"], []); + assert.notEqual(status, 0, "expected tsc to reject assigning UUID.v4() to a number"); + assert.match(output, /wrong\.mts\(3,7\): error TS2322: Type 'string' is not assignable to type 'number'\./); + // The only error is the deliberate one, not a problem in the package's own declarations. + assert.equal(output.trim().split("\n").filter((line) => /error TS\d+/.test(line)).length, 1, output); +}); diff --git a/types/src/lib/versions/rfc/utils.d.mts b/types/src/lib/versions/rfc/utils.d.mts index dd44b0d..d746c87 100644 --- a/types/src/lib/versions/rfc/utils.d.mts +++ b/types/src/lib/versions/rfc/utils.d.mts @@ -9,13 +9,13 @@ export function parse(uuid: string): Uint8Array; /** * Convert array of bytes to UUID string - * @param {Uint8Array|Buffer|Array} bytes - 16-byte array + * @param {ArrayLike} bytes - 16-byte array * @returns {string} UUID string with dashes * @example * stringify([110, 192, 189, 127, 17, 192, 67, 218, 151, 94, 42, 138, 217, 235, 174, 11]); * // => '6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b' */ -export function stringify(bytes: Uint8Array | Buffer | any[]): string; +export function stringify(bytes: ArrayLike): string; /** * Test a string to see if it is a valid UUID * @param {string} uuid - UUID string to validate diff --git a/types/src/lib/versions/rfc/utils.d.mts.map b/types/src/lib/versions/rfc/utils.d.mts.map index e879571..1b04db7 100644 --- a/types/src/lib/versions/rfc/utils.d.mts.map +++ b/types/src/lib/versions/rfc/utils.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/utils.mjs"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,4BANW,MAAM,GACJ,UAAU,CA2BtB;AAED;;;;;;;GAOG;AACH,iCANW,UAAU,GAAC,MAAM,QAAM,GACrB,MAAM,CAelB;AAED;;;;;;;GAOG;AACH,+BANW,MAAM,GACJ,OAAO,CAiBnB;AAED;;;;;;GAMG;AACH,8BALW,MAAM,GACJ,MAAM,GAAC,IAAI,CAavB"} \ No newline at end of file +{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/utils.mjs"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,4BANW,MAAM,GACJ,UAAU,CA2BtB;AAED;;;;;;;GAOG;AACH,iCANW,SAAS,CAAC,MAAM,CAAC,GACf,MAAM,CAelB;AAED;;;;;;;GAOG;AACH,+BANW,MAAM,GACJ,OAAO,CAiBnB;AAED;;;;;;GAMG;AACH,8BALW,MAAM,GACJ,MAAM,GAAC,IAAI,CAavB"} \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v1.d.mts b/types/src/lib/versions/rfc/v1.d.mts index 325d4e5..1495ca4 100644 --- a/types/src/lib/versions/rfc/v1.d.mts +++ b/types/src/lib/versions/rfc/v1.d.mts @@ -1,20 +1,20 @@ /** * Create a version 1 (timestamp) UUID - * @param {Object} options - Optional parameters - * @param {Array} options.node - 6-byte node id (MAC address) - * @param {number} options.clockseq - 14-bit clock sequence - * @param {number} options.msecs - Timestamp in milliseconds since unix epoch - * @param {number} options.nsecs - Additional 100-nanosecond intervals - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {ArrayLike} [options.node] - 6-byte node id (MAC address) + * @param {number} [options.clockseq] - 14-bit clock sequence + * @param {number} [options.msecs] - Timestamp in milliseconds since unix epoch + * @param {number} [options.nsecs] - Additional 100-nanosecond intervals + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v1(options?: { - node: any[]; - clockseq: number; - msecs: number; - nsecs: number; - buf: Uint8Array; - offset: number; + node?: ArrayLike; + clockseq?: number; + msecs?: number; + nsecs?: number; + buf?: Uint8Array; + offset?: number; }): string | Uint8Array; //# sourceMappingURL=v1.d.mts.map \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v1.d.mts.map b/types/src/lib/versions/rfc/v1.d.mts.map index 1ddc193..e69fd31 100644 --- a/types/src/lib/versions/rfc/v1.d.mts.map +++ b/types/src/lib/versions/rfc/v1.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"v1.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v1.mjs"],"names":[],"mappings":"AAwBA;;;;;;;;;;GAUG;AACH,6BARG;IAAuB,IAAI;IACH,QAAQ,EAAxB,MAAM;IACU,KAAK,EAArB,MAAM;IACU,KAAK,EAArB,MAAM;IACc,GAAG,EAAvB,UAAU;IACM,MAAM,EAAtB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CAoD7B"} \ No newline at end of file +{"version":3,"file":"v1.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v1.mjs"],"names":[],"mappings":"AAwBA;;;;;;;;;;GAUG;AACH,6BARG;IAAoC,IAAI,GAAhC,SAAS,CAAC,MAAM,CAAC;IACA,QAAQ,GAAzB,MAAM;IACW,KAAK,GAAtB,MAAM;IACW,KAAK,GAAtB,MAAM;IACe,GAAG,GAAxB,UAAU;IACO,MAAM,GAAvB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CAoD7B"} \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v35.d.mts b/types/src/lib/versions/rfc/v35.d.mts index 806d635..83c110d 100644 --- a/types/src/lib/versions/rfc/v35.d.mts +++ b/types/src/lib/versions/rfc/v35.d.mts @@ -2,18 +2,18 @@ * Create a version 3 (namespace with MD5) UUID * @param {string} name - Name to hash * @param {string|Uint8Array} namespace - Namespace UUID - * @param {Uint8Array} buf - Optional buffer to write into - * @param {number} offset - Optional offset in buffer + * @param {Uint8Array} [buf] - Optional buffer to write into; when given, it is returned instead of a string + * @param {number} [offset] - Optional offset in buffer * @returns {string|Uint8Array} UUID string or buffer */ -export function v3(name: string, namespace: string | Uint8Array, buf: Uint8Array, offset: number): string | Uint8Array; +export function v3(name: string, namespace: string | Uint8Array, buf?: Uint8Array, offset?: number): string | Uint8Array; /** * Create a version 5 (namespace with SHA-1) UUID * @param {string} name - Name to hash * @param {string|Uint8Array} namespace - Namespace UUID - * @param {Uint8Array} buf - Optional buffer to write into - * @param {number} offset - Optional offset in buffer + * @param {Uint8Array} [buf] - Optional buffer to write into; when given, it is returned instead of a string + * @param {number} [offset] - Optional offset in buffer * @returns {string|Uint8Array} UUID string or buffer */ -export function v5(name: string, namespace: string | Uint8Array, buf: Uint8Array, offset: number): string | Uint8Array; +export function v5(name: string, namespace: string | Uint8Array, buf?: Uint8Array, offset?: number): string | Uint8Array; //# sourceMappingURL=v35.d.mts.map \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v35.d.mts.map b/types/src/lib/versions/rfc/v35.d.mts.map index 828d509..e7264b9 100644 --- a/types/src/lib/versions/rfc/v35.d.mts.map +++ b/types/src/lib/versions/rfc/v35.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"v35.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v35.mjs"],"names":[],"mappings":"AAiEA;;;;;;;GAOG;AACH,yBANW,MAAM,aACN,MAAM,GAAC,UAAU,OACjB,UAAU,UACV,MAAM,GACJ,MAAM,GAAC,UAAU,CAI7B;AAED;;;;;;;GAOG;AACH,yBANW,MAAM,aACN,MAAM,GAAC,UAAU,OACjB,UAAU,UACV,MAAM,GACJ,MAAM,GAAC,UAAU,CAI7B"} \ No newline at end of file +{"version":3,"file":"v35.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v35.mjs"],"names":[],"mappings":"AAiEA;;;;;;;GAOG;AACH,yBANW,MAAM,aACN,MAAM,GAAC,UAAU,QACjB,UAAU,WACV,MAAM,GACJ,MAAM,GAAC,UAAU,CAI7B;AAED;;;;;;;GAOG;AACH,yBANW,MAAM,aACN,MAAM,GAAC,UAAU,QACjB,UAAU,WACV,MAAM,GACJ,MAAM,GAAC,UAAU,CAI7B"} \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v4.d.mts b/types/src/lib/versions/rfc/v4.d.mts index 5c9a043..11ce22b 100644 --- a/types/src/lib/versions/rfc/v4.d.mts +++ b/types/src/lib/versions/rfc/v4.d.mts @@ -1,14 +1,14 @@ /** * Create a version 4 (random) UUID - * @param {Object} options - Optional parameters - * @param {Uint8Array} options.random - 16 random bytes - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {Uint8Array} [options.random] - 16 random bytes + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v4(options?: { - random: Uint8Array; - buf: Uint8Array; - offset: number; + random?: Uint8Array; + buf?: Uint8Array; + offset?: number; }): string | Uint8Array; //# sourceMappingURL=v4.d.mts.map \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v4.d.mts.map b/types/src/lib/versions/rfc/v4.d.mts.map index 2f94be3..73d30ed 100644 --- a/types/src/lib/versions/rfc/v4.d.mts.map +++ b/types/src/lib/versions/rfc/v4.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"v4.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v4.mjs"],"names":[],"mappings":"AAwBA;;;;;;;GAOG;AACH,6BALG;IAA4B,MAAM,EAA1B,UAAU;IACU,GAAG,EAAvB,UAAU;IACM,MAAM,EAAtB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CAkB7B"} \ No newline at end of file +{"version":3,"file":"v4.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v4.mjs"],"names":[],"mappings":"AAwBA;;;;;;;GAOG;AACH,6BALG;IAA6B,MAAM,GAA3B,UAAU;IACW,GAAG,GAAxB,UAAU;IACO,MAAM,GAAvB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CAkB7B"} \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v6.d.mts b/types/src/lib/versions/rfc/v6.d.mts index d3e3024..14ba6de 100644 --- a/types/src/lib/versions/rfc/v6.d.mts +++ b/types/src/lib/versions/rfc/v6.d.mts @@ -1,20 +1,20 @@ /** * Create a version 6 (timestamp, reordered) UUID - * @param {Object} options - Optional parameters - * @param {Array} options.node - 6-byte node id (MAC address) - * @param {number} options.clockseq - 14-bit clock sequence - * @param {number} options.msecs - Timestamp in milliseconds since unix epoch - * @param {number} options.nsecs - Additional 100-nanosecond intervals - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {ArrayLike} [options.node] - 6-byte node id (MAC address) + * @param {number} [options.clockseq] - 14-bit clock sequence + * @param {number} [options.msecs] - Timestamp in milliseconds since unix epoch + * @param {number} [options.nsecs] - Additional 100-nanosecond intervals + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v6(options?: { - node: any[]; - clockseq: number; - msecs: number; - nsecs: number; - buf: Uint8Array; - offset: number; + node?: ArrayLike; + clockseq?: number; + msecs?: number; + nsecs?: number; + buf?: Uint8Array; + offset?: number; }): string | Uint8Array; //# sourceMappingURL=v6.d.mts.map \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v6.d.mts.map b/types/src/lib/versions/rfc/v6.d.mts.map index 5eabd42..5e5e9a1 100644 --- a/types/src/lib/versions/rfc/v6.d.mts.map +++ b/types/src/lib/versions/rfc/v6.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"v6.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v6.mjs"],"names":[],"mappings":"AAyBA;;;;;;;;;;GAUG;AACH,6BARG;IAAuB,IAAI;IACH,QAAQ,EAAxB,MAAM;IACU,KAAK,EAArB,MAAM;IACU,KAAK,EAArB,MAAM;IACc,GAAG,EAAvB,UAAU;IACM,MAAM,EAAtB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CA+C7B"} \ No newline at end of file +{"version":3,"file":"v6.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v6.mjs"],"names":[],"mappings":"AAyBA;;;;;;;;;;GAUG;AACH,6BARG;IAAoC,IAAI,GAAhC,SAAS,CAAC,MAAM,CAAC;IACA,QAAQ,GAAzB,MAAM;IACW,KAAK,GAAtB,MAAM;IACW,KAAK,GAAtB,MAAM;IACe,GAAG,GAAxB,UAAU;IACO,MAAM,GAAvB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CA+C7B"} \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v7.d.mts b/types/src/lib/versions/rfc/v7.d.mts index 330806f..423c5e7 100644 --- a/types/src/lib/versions/rfc/v7.d.mts +++ b/types/src/lib/versions/rfc/v7.d.mts @@ -1,14 +1,14 @@ /** * Create a version 7 (Unix Epoch time-based) UUID - * @param {Object} options - Optional parameters - * @param {number} options.msecs - Timestamp in milliseconds since unix epoch - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {number} [options.msecs] - Timestamp in milliseconds since unix epoch + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer */ export function v7(options?: { - msecs: number; - buf: Uint8Array; - offset: number; + msecs?: number; + buf?: Uint8Array; + offset?: number; }): string | Uint8Array; //# sourceMappingURL=v7.d.mts.map \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v7.d.mts.map b/types/src/lib/versions/rfc/v7.d.mts.map index e14751f..18913a8 100644 --- a/types/src/lib/versions/rfc/v7.d.mts.map +++ b/types/src/lib/versions/rfc/v7.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"v7.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v7.mjs"],"names":[],"mappings":"AAyBA;;;;;;;GAOG;AACH,6BALG;IAAwB,KAAK,EAArB,MAAM;IACc,GAAG,EAAvB,UAAU;IACM,MAAM,EAAtB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CA4B7B"} \ No newline at end of file +{"version":3,"file":"v7.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v7.mjs"],"names":[],"mappings":"AAyBA;;;;;;;GAOG;AACH,6BALG;IAAyB,KAAK,GAAtB,MAAM;IACe,GAAG,GAAxB,UAAU;IACO,MAAM,GAAvB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CA4B7B"} \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v8.d.mts b/types/src/lib/versions/rfc/v8.d.mts index 30f4eca..ac4e066 100644 --- a/types/src/lib/versions/rfc/v8.d.mts +++ b/types/src/lib/versions/rfc/v8.d.mts @@ -1,9 +1,9 @@ /** * Create a version 8 (custom/experimental) UUID - * @param {Object} options - Optional parameters - * @param {Uint8Array} options.data - Custom data to fill the UUID (16 bytes) - * @param {Uint8Array} options.buf - Buffer to write UUID into - * @param {number} options.offset - Offset in buffer to start writing + * @param {object} [options] - Optional parameters + * @param {Uint8Array} [options.data] - Custom data to fill the UUID (16 bytes) + * @param {Uint8Array} [options.buf] - Buffer to write UUID into; when given, it is returned instead of a string + * @param {number} [options.offset] - Offset in buffer to start writing * @returns {string|Uint8Array} UUID string or buffer * @example * // Generate with random data @@ -15,8 +15,8 @@ * v8({ data: customData }); */ export function v8(options?: { - data: Uint8Array; - buf: Uint8Array; - offset: number; + data?: Uint8Array; + buf?: Uint8Array; + offset?: number; }): string | Uint8Array; //# sourceMappingURL=v8.d.mts.map \ No newline at end of file diff --git a/types/src/lib/versions/rfc/v8.d.mts.map b/types/src/lib/versions/rfc/v8.d.mts.map index 92f2317..7e5f457 100644 --- a/types/src/lib/versions/rfc/v8.d.mts.map +++ b/types/src/lib/versions/rfc/v8.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"v8.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v8.mjs"],"names":[],"mappings":"AA0BA;;;;;;;;;;;;;;;GAeG;AACH,6BAbG;IAA4B,IAAI,EAAxB,UAAU;IACU,GAAG,EAAvB,UAAU;IACM,MAAM,EAAtB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CA6B7B"} \ No newline at end of file +{"version":3,"file":"v8.d.mts","sourceRoot":"","sources":["../../../../../src/lib/versions/rfc/v8.mjs"],"names":[],"mappings":"AA0BA;;;;;;;;;;;;;;;GAeG;AACH,6BAbG;IAA6B,IAAI,GAAzB,UAAU;IACW,GAAG,GAAxB,UAAU;IACO,MAAM,GAAvB,MAAM;CACd,GAAU,MAAM,GAAC,UAAU,CA6B7B"} \ No newline at end of file diff --git a/types/src/uuid.d.mts b/types/src/uuid.d.mts index ade242c..b5886e1 100644 --- a/types/src/uuid.d.mts +++ b/types/src/uuid.d.mts @@ -1,3 +1,69 @@ +/** + * Options shared by the RFC generators that can write into a caller-supplied buffer. + */ +export type RFCBufferOptions = { + /** + * - Buffer to write the UUID into; when given, the generator returns it instead of a string + */ + buf?: Uint8Array; + /** + * - Offset in `buf` to start writing at (default 0) + */ + offset?: number; +}; +/** + * Options for {@link UUID.v1} and {@link UUID.v6}. `node` is the 6-byte node id (MAC address), + * `clockseq` the 14-bit clock sequence, `msecs` the timestamp in milliseconds since the Unix + * epoch, and `nsecs` additional 100-nanosecond intervals. + */ +export type TimeOptions = RFCBufferOptions & { + node?: ArrayLike; + clockseq?: number; + msecs?: number; + nsecs?: number; +}; +/** + * Options for {@link UUID.v4}. `random` supplies the 16 random bytes instead of generating them. + */ +export type V4Options = RFCBufferOptions & { + random?: Uint8Array; +}; +/** + * Options for {@link UUID.v7}. `msecs` is the timestamp in milliseconds since the Unix epoch. + */ +export type V7Options = RFCBufferOptions & { + msecs?: number; +}; +/** + * Options for {@link UUID.v8}. `data` supplies the 16 bytes of custom data instead of random bytes. + */ +export type V8Options = RFCBufferOptions & { + data?: Uint8Array; +}; +/** + * Options shared by the RFC generators that can write into a caller-supplied buffer. + * @typedef {object} RFCBufferOptions + * @property {Uint8Array} [buf] - Buffer to write the UUID into; when given, the generator returns it instead of a string + * @property {number} [offset] - Offset in `buf` to start writing at (default 0) + */ +/** + * Options for {@link UUID.v1} and {@link UUID.v6}. `node` is the 6-byte node id (MAC address), + * `clockseq` the 14-bit clock sequence, `msecs` the timestamp in milliseconds since the Unix + * epoch, and `nsecs` additional 100-nanosecond intervals. + * @typedef {RFCBufferOptions & { node?: ArrayLike, clockseq?: number, msecs?: number, nsecs?: number }} TimeOptions + */ +/** + * Options for {@link UUID.v4}. `random` supplies the 16 random bytes instead of generating them. + * @typedef {RFCBufferOptions & { random?: Uint8Array }} V4Options + */ +/** + * Options for {@link UUID.v7}. `msecs` is the timestamp in milliseconds since the Unix epoch. + * @typedef {RFCBufferOptions & { msecs?: number }} V7Options + */ +/** + * Options for {@link UUID.v8}. `data` supplies the 16 bytes of custom data instead of random bytes. + * @typedef {RFCBufferOptions & { data?: Uint8Array }} V8Options + */ /** * UUID class implementing the new specification */ @@ -6,63 +72,63 @@ export class UUID { * Create a new Issuer Variant UUID * @param {number} issuerID - Issuer ID (0-ISSUER_ID_MASK) * @param {number} version - Version number - * @param {Uint8Array} entropy - Additional entropy data + * @param {Uint8Array|null} [entropy] - Additional entropy data * @returns {UUID} New UUID instance */ - static createIssuerVariant(issuerID: number, version: number, entropy?: Uint8Array): UUID; + static createIssuerVariant(issuerID: number, version: number, entropy?: Uint8Array | null): UUID; /** * Create a new Timestamp Variant UUID - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) + * @param {number|Date|null|undefined} timestamp - Timestamp value (null/undefined defaults to the current time) * @param {number} version - Version number - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ - static createTimestampVariant(timestamp: number | Date, version: number, entropy?: Uint8Array): UUID; + static createTimestampVariant(timestamp: number | Date | null | undefined, version: number, entropy?: Uint8Array | null): UUID; /** * Create an issuer-based UUID (short name alias) * @param {number} issuerID - Issuer ID (0-1023) * @param {number} version - Version number - * @param {Uint8Array} entropy - Additional entropy data + * @param {Uint8Array|null} [entropy] - Additional entropy data * @returns {UUID} New UUID instance */ - static issuer(issuerID: number, version: number, entropy?: Uint8Array): UUID; + static issuer(issuerID: number, version: number, entropy?: Uint8Array | null): UUID; /** * Create a timestamp-based UUID (short name alias) - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) + * @param {number|Date|null|undefined} timestamp - Timestamp value (null/undefined defaults to the current time) * @param {number} version - Version number - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ - static timestamp(timestamp: number | Date, version: number, entropy?: Uint8Array): UUID; + static timestamp(timestamp: number | Date | null | undefined, version: number, entropy?: Uint8Array | null): UUID; /** * Create Timestamp Variant v1 UUID (ultra-short alias) * Subvariant 00 - Timestamp-based identification (seconds precision) - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {number|Date|null} [timestamp] - Timestamp value (optional, defaults to the current time) + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ - static TA(timestamp: number | Date, entropy?: Uint8Array): UUID; + static TA(timestamp?: number | Date | null, entropy?: Uint8Array | null): UUID; /** * Create Issuer Variant v1 UUID (ultra-short alias) * Subvariant 01 - Issuer-based identification * @param {number} issuerID - Issuer ID (0-1023) - * @param {Uint8Array} entropy - Additional entropy data + * @param {Uint8Array|null} [entropy] - Additional entropy data * @returns {UUID} New UUID instance */ - static IA(issuerID: number, entropy?: Uint8Array): UUID; + static IA(issuerID: number, entropy?: Uint8Array | null): UUID; /** * Create Timestamp Variant v2 UUID (ultra-short alias) * Subvariant 00 - Timestamp-based identification (milliseconds precision) - * @param {number|Date} timestamp - Timestamp value (optional, defaults to Date.now()) - * @param {Uint8Array} entropy - Optional entropy for bits 79-127 + * @param {number|Date|null} [timestamp] - Timestamp value (optional, defaults to the current time) + * @param {Uint8Array|null} [entropy] - Optional entropy for bits 79-127 * @returns {UUID} New UUID instance */ - static TB(timestamp: number | Date, entropy?: Uint8Array): UUID; + static TB(timestamp?: number | Date | null, entropy?: Uint8Array | null): UUID; /** * Get the shared issuer registry instance - * @returns {Promise} Shared registry instance + * @returns {Promise} Shared registry instance */ - static getRegistry(): Promise; + static getRegistry(): Promise; /** * Register a new issuer in Category A * @param {number} issuerID - Issuer ID (2-255) @@ -99,10 +165,22 @@ export class UUID { static validateDetailed(buffer: Uint8Array): Promise; /** * Generate a version 1 (timestamp) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {TimeOptions & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ - static v1(options: any): string; + static v1(options?: TimeOptions & { + buf?: undefined; + }): string; + /** + * Generate a version 1 (timestamp) UUID, written into `options.buf` + * @overload + * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + static v1(options: TimeOptions & { + buf: Uint8Array; + }): Uint8Array; /** * Generate a version 3 (namespace with MD5) UUID * @param {string} name - Name to hash @@ -112,10 +190,22 @@ export class UUID { static v3(name: string, namespace: string | Uint8Array): string; /** * Generate a version 4 (random) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {V4Options & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ - static v4(options: any): string; + static v4(options?: V4Options & { + buf?: undefined; + }): string; + /** + * Generate a version 4 (random) UUID, written into `options.buf` + * @overload + * @param {V4Options & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + static v4(options: V4Options & { + buf: Uint8Array; + }): Uint8Array; /** * Generate a version 5 (namespace with SHA-1) UUID * @param {string} name - Name to hash @@ -125,22 +215,58 @@ export class UUID { static v5(name: string, namespace: string | Uint8Array): string; /** * Generate a version 6 (timestamp, reordered) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {TimeOptions & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ - static v6(options: any): string; + static v6(options?: TimeOptions & { + buf?: undefined; + }): string; + /** + * Generate a version 6 (timestamp, reordered) UUID, written into `options.buf` + * @overload + * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + static v6(options: TimeOptions & { + buf: Uint8Array; + }): Uint8Array; /** * Generate a version 7 (Unix Epoch) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {V7Options & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ - static v7(options: any): string; + static v7(options?: V7Options & { + buf?: undefined; + }): string; + /** + * Generate a version 7 (Unix Epoch) UUID, written into `options.buf` + * @overload + * @param {V7Options & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + static v7(options: V7Options & { + buf: Uint8Array; + }): Uint8Array; /** * Generate a version 8 (custom/experimental) UUID - * @param {Object} options - Optional parameters + * @overload + * @param {V8Options & { buf?: undefined }} [options] - Optional parameters * @returns {string} UUID string */ - static v8(options: any): string; + static v8(options?: V8Options & { + buf?: undefined; + }): string; + /** + * Generate a version 8 (custom/experimental) UUID, written into `options.buf` + * @overload + * @param {V8Options & { buf: Uint8Array }} options - Options with the buffer to write into + * @returns {Uint8Array} `options.buf` + */ + static v8(options: V8Options & { + buf: Uint8Array; + }): Uint8Array; /** * Convert UUID string to byte array * @param {string} uuid - UUID string @@ -149,10 +275,10 @@ export class UUID { static parse(uuid: string): Uint8Array; /** * Convert byte array to UUID string - * @param {Uint8Array|Buffer|Array} bytes - 16-byte array + * @param {ArrayLike} bytes - 16-byte array (Uint8Array, Buffer or plain array) * @returns {string} UUID string */ - static stringify(bytes: Uint8Array | Buffer | any[]): string; + static stringify(bytes: ArrayLike): string; /** * Validate UUID string format * @param {string} uuid - UUID string to validate @@ -161,7 +287,7 @@ export class UUID { static validateRFC(uuid: string): boolean; /** * Detect version/variant identifier of UUID (handles both RFC and custom variants) - * @param {string|Buffer|UUID} uuid - UUID string, buffer, or UUID instance + * @param {string|Uint8Array|UUID} uuid - UUID string, buffer, or UUID instance * @returns {string|number|null} Version identifier (e.g., "TA", "TB", "IA" for custom, 1-8 for RFC, or null if invalid) * @example * UUID.version(uuidString); // => "TA" for Timestamp v1 @@ -169,20 +295,25 @@ export class UUID { * UUID.version(uuidString); // => "IA" for Issuer v1 * UUID.version(uuidString); // => 4 for RFC v4 */ - static version(uuid: string | Buffer | UUID): string | number | null; + static version(uuid: string | Uint8Array | UUID): string | number | null; /** * Detect variant identifier (alias for version()) - * @param {string|Buffer|UUID} uuid - UUID string, buffer, or UUID instance + * @param {string|Uint8Array|UUID} uuid - UUID string, buffer, or UUID instance * @returns {string|number|null} Version identifier * @deprecated Use UUID.version() instead */ - static detectVariant(uuid: string | Buffer | UUID): string | number | null; + static detectVariant(uuid: string | Uint8Array | UUID): string | number | null; /** * Create a new UUID instance * @param {Uint8Array|string|null} data - Optional UUID data to parse */ constructor(data?: Uint8Array | string | null); - _buffer: Uint8Array; + /** + * The 16 UUID bytes. + * @type {Uint8Array} + * @private + */ + private _buffer; /** * Parse UUID from existing data * @param {Uint8Array|string} data - UUID data to parse @@ -197,7 +328,7 @@ export class UUID { private _setTimestamp; /** * Fill remaining bits with entropy while preserving immutable fields - * @param {Uint8Array} entropy - Entropy data + * @param {Uint8Array|null} [entropy] - Entropy data * @private */ private _fillEntropy; @@ -287,9 +418,9 @@ export class UUID { toString(): string; /** * Convert UUID to buffer - * @returns {Buffer} UUID as 16-byte buffer (Node); a Uint8Array copy in environments without Buffer + * @returns {Uint8Array} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer */ - toBuffer(): Buffer; + toBuffer(): Uint8Array; /** * Return the primitive value of the UUID (string representation) * This allows UUIDs to be automatically converted to strings when used in string contexts diff --git a/types/src/uuid.d.mts.map b/types/src/uuid.d.mts.map index a122b6e..2266928 100644 --- a/types/src/uuid.d.mts.map +++ b/types/src/uuid.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"uuid.d.mts","sourceRoot":"","sources":["../../src/uuid.mjs"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"uuid.d.mts","sourceRoot":"","sources":["../../src/uuid.mjs"],"names":[],"mappings":"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"} \ No newline at end of file From 0d480541859811f76d0104c729d656f562cacf24 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 18:08:49 -0700 Subject: [PATCH 08/15] fix(types): type byte returns as Buffer for Node consumers, Uint8Array elsewhere toBuffer() returns a Buffer in Node but was typed Uint8Array everywhere, so Node consumers could not call Buffer APIs on it without a cast. A `Bytes` alias now comes from the private `#bytes-type` import, mapped in package.json `imports` (types only): typings/bytes-node.d.mts (Buffer) under the `node` condition, which TypeScript applies with node16/nodenext, and typings/bytes.d.mts (Uint8Array) otherwise (bundler resolution, or the `browser` condition). toBuffer() returns `Bytes`. The `buf` overloads of v1/v4/v6/v7/v8 are generic and return the buffer they were given, so a Buffer stays a Buffer and a plain Uint8Array is not mistyped as one. typings/ ships in `files`. The consumer type test checks the main entry under nodenext with @types/node and under bundler without it, that Buffer methods compile on the Node flavour, that they fail on the default flavour, and which typings file each mode resolves. Refs #51 --- package.json | 10 ++++ src/uuid.mjs | 34 ++++++++----- tests/types/consumer.test.mjs | 92 +++++++++++++++++++++++++++++++++-- types/src/uuid.d.mts | 71 +++++++++++++++++---------- types/src/uuid.d.mts.map | 2 +- typings/bytes-node.d.mts | 23 +++++++++ typings/bytes.d.mts | 21 ++++++++ 7 files changed, 209 insertions(+), 44 deletions(-) create mode 100644 typings/bytes-node.d.mts create mode 100644 typings/bytes.d.mts diff --git a/package.json b/package.json index 63d3db1..75b0803 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,15 @@ }, "./package.json": "./package.json" }, + "imports": { + "#bytes-type": { + "types": { + "browser": "./typings/bytes.d.mts", + "node": "./typings/bytes-node.d.mts", + "default": "./typings/bytes.d.mts" + } + } + }, "type": "module", "engines": { "node": ">=16.12.0" @@ -126,6 +135,7 @@ "types/dist/", "types/index.d.mts", "types/index.d.mts.map", + "typings/", "dist/" ], "sideEffects": false, diff --git a/src/uuid.mjs b/src/uuid.mjs index ad75246..1723583 100644 --- a/src/uuid.mjs +++ b/src/uuid.mjs @@ -44,6 +44,13 @@ import { } from "./lib/constants.mjs"; import * as rfcUuids from "./lib/versions/rfc/index.mjs"; +/** + * The byte container toBuffer() returns: a Buffer in Node, a plain Uint8Array elsewhere. + * `#bytes-type` (package.json `imports`) resolves to a Buffer alias under the `node` + * condition and to a Uint8Array alias otherwise. + * @typedef {import("#bytes-type").Bytes} Bytes + */ + /** * Options shared by the RFC generators that can write into a caller-supplied buffer. * @typedef {object} RFCBufferOptions @@ -566,7 +573,7 @@ class UUID { /** * Convert UUID to buffer - * @returns {Uint8Array} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer + * @returns {Bytes} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer */ toBuffer() { return toBufferLike(this._buffer); @@ -714,9 +721,10 @@ class UUID { */ /** * Generate a version 1 (timestamp) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {TimeOptions & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ /** * Generate a version 1 (timestamp) UUID @@ -745,9 +753,10 @@ class UUID { */ /** * Generate a version 4 (random) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {V4Options & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {V4Options & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ /** * Generate a version 4 (random) UUID @@ -776,9 +785,10 @@ class UUID { */ /** * Generate a version 6 (timestamp, reordered) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {TimeOptions & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ /** * Generate a version 6 (timestamp, reordered) UUID @@ -797,9 +807,10 @@ class UUID { */ /** * Generate a version 7 (Unix Epoch) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {V7Options & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {V7Options & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ /** * Generate a version 7 (Unix Epoch) UUID @@ -818,9 +829,10 @@ class UUID { */ /** * Generate a version 8 (custom/experimental) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {V8Options & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {V8Options & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ /** * Generate a version 8 (custom/experimental) UUID diff --git a/tests/types/consumer.test.mjs b/tests/types/consumer.test.mjs index 3224552..675eb62 100644 --- a/tests/types/consumer.test.mjs +++ b/tests/types/consumer.test.mjs @@ -182,6 +182,52 @@ export { random, hex, back, copy, md5Digest, sha1Digest }; assert.ok(example, "README.md has no ```typescript block under its TypeScript Support heading"); writeFileSync(path.join(consumerDir, "readme.mts"), `${example[1]}\nexport {};\n`); + // Node flavour: under nodenext the "node" condition picks the declarations where the + // runtime byte container is a Buffer, so Buffer-only APIs compile. + writeFileSync( + path.join(consumerDir, "node.mts"), + `import { UUID } from "@cldmv/uuid"; + +const length: number = UUID.v4().length; +const ta = UUID.TA(); +const tb = UUID.TB(new Date()); +const bytes: Buffer = ta.toBuffer(); +const hex: string = tb.toBuffer().toString("hex"); +const v4Hex: string = UUID.v4({ buf: Buffer.alloc(16) }).toString("hex"); +const v7Hex: string = UUID.v7({ buf: Buffer.alloc(32), offset: 16 }).toString("hex"); +// A plain Uint8Array passed as buf comes back as exactly that, not as a Buffer. +const plain: Uint8Array = UUID.v1({ buf: new Uint8Array(16) }); + +export { length, bytes, hex, v4Hex, v7Hex, plain }; +` + ); + + // Default flavour: without the "node" condition (bundler resolution, browsers) the byte + // container is a plain Uint8Array, so Buffer-only APIs must not compile. + writeFileSync( + path.join(consumerDir, "bundler-bytes.mts"), + `import { UUID } from "@cldmv/uuid"; + +const length: number = UUID.v4().length; +const bytes: Uint8Array = UUID.TA().toBuffer(); +const written: Uint8Array = UUID.v4({ buf: new Uint8Array(16) }); + +export { length, bytes, written }; +` + ); + + writeFileSync( + path.join(consumerDir, "bundler-wrong.mts"), + `import { UUID } from "@cldmv/uuid"; + +const hex: string = UUID.TA().toBuffer().toString("hex"); +const first: number = UUID.TA().toBuffer().readUInt8(0); +const written: string = UUID.v4({ buf: new Uint8Array(16) }).toString("hex"); + +export { hex, first, written }; +` + ); + writeFileSync( path.join(consumerDir, "wrong.mts"), `import { UUID } from "@cldmv/uuid"; @@ -197,13 +243,47 @@ after(() => { rmSync(workDir, { recursive: true, force: true }); }); -test("the main entry and ./main type-check without @types/node", () => { - // types: [] keeps @types/node out, so this also shows the UUID declarations do not - // depend on Node-only globals such as Buffer (the package also runs in browsers). - const { status, output } = compile("main", ["main.mts"], []); +/** Compiler options for a bundler-resolution (browser/bundler) consumer. */ +const bundler = { module: "esnext", moduleResolution: "bundler" }; + +test("the main entry and ./main type-check under nodenext with @types/node", () => { + const { status, output } = compile("main", ["main.mts"], ["node"]); + assert.equal(status, 0, output); +}); + +test("the main entry and ./main type-check under bundler resolution without @types/node", () => { + // types: [] keeps @types/node out, so this also shows the default (non-Node) declarations + // do not depend on Node-only globals such as Buffer (the package also runs in browsers). + const { status, output } = compile("main-bundler", ["main.mts"], [], bundler); assert.equal(status, 0, output); }); +test("under nodenext the byte returns are Buffers", () => { + // The "node" condition serves the Node flavour: toBuffer() returns a Buffer, and a buf + // overload returns the buffer it was given, so Buffer-only APIs compile. + const { status, output } = compile("node", ["node.mts"], ["node"], { explainFiles: true }); + assert.equal(status, 0, output); + assert.match(output, /typings\/bytes-node\.d\.mts\n/, "expected #bytes-type to resolve to the Node flavour"); + assert.doesNotMatch(output, /typings\/bytes\.d\.mts\n/, output); +}); + +test("under bundler resolution the byte returns are plain Uint8Arrays", () => { + const { status, output } = compile("bundler-bytes", ["bundler-bytes.mts"], [], { ...bundler, explainFiles: true }); + assert.equal(status, 0, output); + assert.match(output, /typings\/bytes\.d\.mts\n/, "expected #bytes-type to resolve to the default flavour"); + assert.doesNotMatch(output, /typings\/bytes-node\.d\.mts\n/, output); +}); + +test("under bundler resolution Buffer-only methods on the byte returns fail to compile", () => { + // If the default flavour leaked Buffer, these would compile. + const { status, output } = compile("bundler-wrong", ["bundler-wrong.mts"], [], bundler); + assert.notEqual(status, 0, "expected tsc to reject Buffer-only methods on a Uint8Array"); + assert.match(output, /bundler-wrong\.mts\(3,51\): error TS2554: Expected 0 arguments, but got 1\./); + assert.match(output, /bundler-wrong\.mts\(4,44\): error TS2339: Property 'readUInt8' does not exist on type 'Bytes'\./); + assert.match(output, /bundler-wrong\.mts\(5,71\): error TS2554: Expected 0 arguments, but got 1\./); + assert.equal(output.trim().split("\n").filter((line) => /error TS\d+/.test(line)).length, 3, output); +}); + test("the README TypeScript example type-checks", () => { // noUnusedLocals is off by default, so the example's unused bindings are fine. const { status, output } = compile("readme", ["readme.mts"], []); @@ -220,7 +300,9 @@ test("the ./rng, ./bytes and ./hash subpaths type-check", () => { test("the browser variants of ./rng, ./bytes and ./hash type-check without @types/node", () => { // With the "browser" condition the browser declarations are picked, which return plain // Uint8Arrays and must not need Node's globals. - const { status, output } = compile("browser", ["subpaths.mts"], [], { customConditions: ["browser"] }); + // main.mts is included too: with "browser" the main entry gets the Uint8Array flavour even + // under nodenext, so it compiles without @types/node. + const { status, output } = compile("browser", ["subpaths.mts", "main.mts"], [], { customConditions: ["browser"] }); assert.equal(status, 0, output); }); diff --git a/types/src/uuid.d.mts b/types/src/uuid.d.mts index b5886e1..cd361ca 100644 --- a/types/src/uuid.d.mts +++ b/types/src/uuid.d.mts @@ -1,3 +1,9 @@ +/** + * The byte container toBuffer() returns: a Buffer in Node, a plain Uint8Array elsewhere. + * `#bytes-type` (package.json `imports`) resolves to a Buffer alias under the `node` + * condition and to a Uint8Array alias otherwise. + */ +export type Bytes = import("#bytes-type").Bytes; /** * Options shared by the RFC generators that can write into a caller-supplied buffer. */ @@ -40,6 +46,12 @@ export type V7Options = RFCBufferOptions & { export type V8Options = RFCBufferOptions & { data?: Uint8Array; }; +/** + * The byte container toBuffer() returns: a Buffer in Node, a plain Uint8Array elsewhere. + * `#bytes-type` (package.json `imports`) resolves to a Buffer alias under the `node` + * condition and to a Uint8Array alias otherwise. + * @typedef {import("#bytes-type").Bytes} Bytes + */ /** * Options shared by the RFC generators that can write into a caller-supplied buffer. * @typedef {object} RFCBufferOptions @@ -174,13 +186,14 @@ export class UUID { }): string; /** * Generate a version 1 (timestamp) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {TimeOptions & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ - static v1(options: TimeOptions & { - buf: Uint8Array; - }): Uint8Array; + static v1(options: TimeOptions & { + buf: T; + }): T; /** * Generate a version 3 (namespace with MD5) UUID * @param {string} name - Name to hash @@ -199,13 +212,14 @@ export class UUID { }): string; /** * Generate a version 4 (random) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {V4Options & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {V4Options & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ - static v4(options: V4Options & { - buf: Uint8Array; - }): Uint8Array; + static v4(options: V4Options & { + buf: T; + }): T; /** * Generate a version 5 (namespace with SHA-1) UUID * @param {string} name - Name to hash @@ -224,13 +238,14 @@ export class UUID { }): string; /** * Generate a version 6 (timestamp, reordered) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {TimeOptions & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {TimeOptions & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ - static v6(options: TimeOptions & { - buf: Uint8Array; - }): Uint8Array; + static v6(options: TimeOptions & { + buf: T; + }): T; /** * Generate a version 7 (Unix Epoch) UUID * @overload @@ -242,13 +257,14 @@ export class UUID { }): string; /** * Generate a version 7 (Unix Epoch) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {V7Options & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {V7Options & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ - static v7(options: V7Options & { - buf: Uint8Array; - }): Uint8Array; + static v7(options: V7Options & { + buf: T; + }): T; /** * Generate a version 8 (custom/experimental) UUID * @overload @@ -260,13 +276,14 @@ export class UUID { }): string; /** * Generate a version 8 (custom/experimental) UUID, written into `options.buf` + * @template {Uint8Array} T * @overload - * @param {V8Options & { buf: Uint8Array }} options - Options with the buffer to write into - * @returns {Uint8Array} `options.buf` + * @param {V8Options & { buf: T }} options - Options with the buffer to write into + * @returns {T} `options.buf` itself, so its type is kept (a Buffer stays a Buffer) */ - static v8(options: V8Options & { - buf: Uint8Array; - }): Uint8Array; + static v8(options: V8Options & { + buf: T; + }): T; /** * Convert UUID string to byte array * @param {string} uuid - UUID string @@ -418,9 +435,9 @@ export class UUID { toString(): string; /** * Convert UUID to buffer - * @returns {Uint8Array} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer + * @returns {Bytes} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer */ - toBuffer(): Uint8Array; + toBuffer(): Bytes; /** * Return the primitive value of the UUID (string representation) * This allows UUIDs to be automatically converted to strings when used in string contexts diff --git a/types/src/uuid.d.mts.map b/types/src/uuid.d.mts.map index 2266928..185cdb0 100644 --- a/types/src/uuid.d.mts.map +++ b/types/src/uuid.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"uuid.d.mts","sourceRoot":"","sources":["../../src/uuid.mjs"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"uuid.d.mts","sourceRoot":"","sources":["../../src/uuid.mjs"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/typings/bytes-node.d.mts b/typings/bytes-node.d.mts new file mode 100644 index 0000000..66c93bf --- /dev/null +++ b/typings/bytes-node.d.mts @@ -0,0 +1,23 @@ +/** + * + * @Project: @cldmv/uuid + * @Filename: /typings/bytes-node.d.mts + * @Date: 2026-10-03T18:00:00-07:00 (1791075600) + * @Author: Nate Corcoran + * @Email: + * ----- + * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-10-03T18:00:00-07:00 (1791075600) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + * + */ + +/// + +/** + * The byte container the package returns in Node: a Buffer (a Uint8Array subclass). + * package.json `imports` maps `#bytes-type` here under the `node` condition, which + * TypeScript applies with `module`/`moduleResolution` `node16`/`nodenext`. + */ +export type Bytes = Buffer; diff --git a/typings/bytes.d.mts b/typings/bytes.d.mts new file mode 100644 index 0000000..628a37f --- /dev/null +++ b/typings/bytes.d.mts @@ -0,0 +1,21 @@ +/** + * + * @Project: @cldmv/uuid + * @Filename: /typings/bytes.d.mts + * @Date: 2026-10-03T18:00:00-07:00 (1791075600) + * @Author: Nate Corcoran + * @Email: + * ----- + * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-10-03T18:00:00-07:00 (1791075600) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + * + */ + +/** + * The byte container the package returns (toBuffer(), the RFC generators' `buf` results) + * outside Node: a plain Uint8Array. package.json `imports` maps `#bytes-type` here unless + * the consumer resolves with the `node` condition (see bytes-node.d.mts). + */ +export type Bytes = Uint8Array; From 007a5130f72595951cf75e8fea622216e30e66b4 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 18:11:48 -0700 Subject: [PATCH 09/15] fix(types): read Buffer from the global scope instead of referencing @types/node The Node flavour of #bytes-type used /// , so a nodenext project without @types/node got TS2688 from the package, and any project that imported it had Node's globals (process, Buffer, ...) forced in. Bytes is now Buffer when @types/node is loaded and Uint8Array otherwise. A new consumer test checks that importing the package under nodenext without @types/node does not make process available. Refs #51 --- tests/types/consumer.test.mjs | 20 ++++++++++++++++++++ typings/bytes-node.d.mts | 10 +++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/tests/types/consumer.test.mjs b/tests/types/consumer.test.mjs index 675eb62..7971680 100644 --- a/tests/types/consumer.test.mjs +++ b/tests/types/consumer.test.mjs @@ -204,6 +204,16 @@ export { length, bytes, hex, v4Hex, v7Hex, plain }; // Default flavour: without the "node" condition (bundler resolution, browsers) the byte // container is a plain Uint8Array, so Buffer-only APIs must not compile. + writeFileSync( + path.join(consumerDir, "node-no-types.mts"), + `import { UUID } from "@cldmv/uuid"; +const id: string = UUID.v4(); +const bytes: Uint8Array = UUID.TA().toBuffer(); +const env = process.env; +export { id, bytes, env }; +` + ); + writeFileSync( path.join(consumerDir, "bundler-bytes.mts"), `import { UUID } from "@cldmv/uuid"; @@ -267,6 +277,16 @@ test("under nodenext the byte returns are Buffers", () => { assert.doesNotMatch(output, /typings\/bytes\.d\.mts\n/, output); }); +test("under nodenext without @types/node the package does not pull in Node globals", () => { + // The Node flavour must read Buffer from the global scope rather than reference + // @types/node: a Node project without @types/node still compiles against the package, + // and importing it does not make Node globals such as \`process\` appear. + const { status, output } = compile("node-no-types", ["node-no-types.mts"], []); + assert.notEqual(status, 0, "expected tsc to reject the Node global"); + assert.match(output, /node-no-types\.mts\(4,\d+\): error TS2(580|591)/, output); + assert.equal(output.trim().split("\n").filter((line) => /error TS\d+/.test(line)).length, 1, output); +}); + test("under bundler resolution the byte returns are plain Uint8Arrays", () => { const { status, output } = compile("bundler-bytes", ["bundler-bytes.mts"], [], { ...bundler, explainFiles: true }); assert.equal(status, 0, output); diff --git a/typings/bytes-node.d.mts b/typings/bytes-node.d.mts index 66c93bf..d90adca 100644 --- a/typings/bytes-node.d.mts +++ b/typings/bytes-node.d.mts @@ -13,11 +13,15 @@ * */ -/// - /** * The byte container the package returns in Node: a Buffer (a Uint8Array subclass). * package.json `imports` maps `#bytes-type` here under the `node` condition, which * TypeScript applies with `module`/`moduleResolution` `node16`/`nodenext`. + * + * Buffer is read from the global scope instead of through `/// `, + * so importing this package never forces Node's globals into a project. With @types/node + * loaded, `Bytes` is Buffer; without it, it falls back to Uint8Array (which Buffer extends). */ -export type Bytes = Buffer; +type NodeBuffer = typeof globalThis extends { Buffer: { alloc(size: number): infer B } } ? B : never; + +export type Bytes = [NodeBuffer] extends [never] ? Uint8Array : NodeBuffer; From 131f2ece72fc724a7f0b67206851365a79a0c6b0 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 18:45:55 -0700 Subject: [PATCH 10/15] docs: cover #54 and #55 in the v1.2.5 notes and README The LICENSE file and the TypeScript declaration fix merged into next ahead of the v1.2.5 notes but were not described. Add both to the changelog and the What's New block, and show the GitHub license badge now that the file exists. --- README.md | 7 +++++-- docs/changelog/v1/v1.2.5.md | 25 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dd39940..2fe4a8d 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ The custom variants (`TA`, `TB`, `IA`) live in the variant `111` namespace, so t ### Latest: v1.2.5 (October 2026) - **`require()` works**: the CommonJS entry failed to load in every earlier release, because the ESM entry it wraps used top-level `await`, which Node's synchronous `require(esm)` rejects with `ERR_REQUIRE_ASYNC_MODULE`. The entry no longer uses top-level `await`, so `require("@cldmv/uuid")` now returns the same `UUID` object as `import` on Node.js ^20.19.0 or >=22.12.0, and older versions get a clear error that points to `import()`. ESM behavior and the exported names are unchanged ([#50](https://github.com/CLDMV/uuid/pull/50)). +- **Real TypeScript types**: `UUID` used to resolve to `any`. The package and its subpaths now ship accurate declarations, with optional generator options and `toBuffer()` typed as `Buffer` in Node projects and `Uint8Array` in browser and bundler projects ([#55](https://github.com/CLDMV/uuid/pull/55)). The repository also gains its Apache-2.0 `LICENSE` file ([#54](https://github.com/CLDMV/uuid/pull/54)). - [View full v1.2.5 Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.5.md) ### Recent Releases @@ -1101,9 +1102,9 @@ Contributions to the specification and implementation are welcome! This project ## ๐Ÿ“„ License -[![npm license]][npm_license_url] +[![GitHub license]][github_license_url] [![npm license]][npm_license_url] -Apache-2.0 ยฉ Shinrai / CLDMV +Apache-2.0 ยฉ Shinrai / CLDMV. See [LICENSE](https://github.com/CLDMV/uuid/blob/master/LICENSE) for the full text. This specification and implementation are provided for RFC standardization consideration. @@ -1138,6 +1139,8 @@ Made with โค๏ธ by [CLDMV](https://cldmv.net) [npm_size_url]: https://www.npmjs.com/package/@cldmv/uuid [repo size]: https://img.shields.io/github/repo-size/CLDMV/uuid?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 [repo_size_url]: https://github.com/CLDMV/uuid +[github license]: https://img.shields.io/github/license/CLDMV/uuid.svg?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 +[github_license_url]: https://github.com/CLDMV/uuid/blob/HEAD/LICENSE [npm license]: https://img.shields.io/npm/l/%40cldmv%2Fuuid.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 [npm_license_url]: https://www.npmjs.com/package/@cldmv/uuid [coverage]: https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FCLDMV%2Fuuid%2Fbadges%2Fcoverage.json&style=for-the-badge&logo=vitest&logoColor=white diff --git a/docs/changelog/v1/v1.2.5.md b/docs/changelog/v1/v1.2.5.md index 6a41b1d..7da58b0 100644 --- a/docs/changelog/v1/v1.2.5.md +++ b/docs/changelog/v1/v1.2.5.md @@ -10,7 +10,9 @@ v1.2.5 makes `require("@cldmv/uuid")` work. The CommonJS entry has failed to load in every release up to and including v1.2.4, because the ESM entry it wraps used top-level `await`. This release removes the top-level `await`, so `require()` returns the same `UUID` object that `import` does on any Node.js version with synchronous `require(esm)`, and fails with a clear message on versions without it. -ESM consumers see no change in behavior. The exported names (`default`, `UUID`, `uuid`, `ISSUER_CATEGORIES`) and their values are the same as in v1.2.4. +It also gives TypeScript users real types for the first time: `UUID` used to resolve to `any`, and it is now fully typed, with `Buffer` returns in Node projects and `Uint8Array` returns in browser and bundler projects. The repository finally carries the Apache-2.0 `LICENSE` file that `package.json` has always declared. + +ESM consumers see no change in runtime behavior. The exported names (`default`, `UUID`, `uuid`, `ISSUER_CATEGORIES`) and their values are the same as in v1.2.4. --- @@ -29,6 +31,26 @@ ESM consumers see no change in behavior. The exported names (`default`, `UUID`, `tsc` emitted `export default UUID;` above the declaration it referred to in `types/index.d.mts`, which CodeQL flags as `js/use-before-declaration`. The entry now exports a single list (`export { UUID as default, UUID, UUID as uuid, ISSUER_CATEGORIES }`), so the generated declaration file imports `UUID` and `ISSUER_CATEGORIES` from `@cldmv/uuid/main` first and then re-exports them. The exported types are unchanged. +## ๐Ÿ”ท TypeScript + +### Accurate type declarations for the package and its subpaths ([#55](https://github.com/CLDMV/uuid/pull/55), fixes [#51](https://github.com/CLDMV/uuid/issues/51)) + +`types/index.d.mts` imports from `@cldmv/uuid/main`, but `./main` had no `types` condition, so TypeScript could not find its declarations and `UUID` was typed `any`. `skipLibCheck` hid the error. `./main`, `./rng`, `./bytes` and `./hash` now all have `types` conditions, and the generated declarations were fixed in the source JSDoc so they compile with `strict` and `skipLibCheck: false`: + +- The options argument of `v1`, `v4`, `v6`, `v7` and `v8` is optional and typed (`UUID.v4()` used to be a compile error once the types resolved). Passing `buf` returns that same buffer, typed as whatever you passed in; otherwise the methods return a `string`. +- `TA`/`TB` timestamps and `entropy` arguments are optional, and `getRegistry()`'s `IssuerRegistry` type resolves. +- `toBuffer()` returns `Buffer` in Node projects (`module`/`moduleResolution` `node16`/`nodenext` with `@types/node`) and `Uint8Array` in browser and bundler projects, chosen through a private `#bytes-type` import. The package never forces Node's globals into a project: without `@types/node` the type falls back to `Uint8Array`. + +A new `npm run test:types` check (run by `npm test` and `npm run coverage`) packs the package, installs it into a throwaway consumer and compiles it under `nodenext` and `bundler` resolution, including the README's TypeScript example. + +--- + +## ๐Ÿ“„ License + +The repository now includes the Apache-2.0 `LICENSE` file ([#54](https://github.com/CLDMV/uuid/pull/54)). `package.json` has always declared Apache-2.0 and listed `LICENSE` in `files`, but the file was missing, so GitHub showed no license and the published package shipped without the license text. + +--- + ## ๐Ÿ“š Documentation - **NEW:** [docs/changelog/v1/v1.2.5.md](./v1.2.5.md): this changelog. @@ -40,4 +62,5 @@ ESM consumers see no change in behavior. The exported names (`default`, `UUID`, ## Upgrade notes - No breaking changes. ESM imports behave exactly as in v1.2.4. +- TypeScript projects that relied on `UUID` being `any` may now see real type errors in their own code; those are calls the types previously failed to check. - CommonJS consumers can now use `const UUID = require("@cldmv/uuid")` (with `UUID.UUID`, `UUID.uuid` and `UUID.ISSUER_CATEGORIES` as properties). This needs Node.js ^20.19.0 or >=22.12.0. On older Node.js versions, load the package with `import()`. From 3ea1f7d8d7fec91d36acfe59693653ad1dba9d04 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 20:01:49 -0700 Subject: [PATCH 11/15] deps: bump @cldmv/fix-headers to 2.1.4 Bumps @cldmv/fix-headers to 2.1.4 (JSON/Markdown/extension-less files never get a JS comment; dependency folders never walked). Ran fix:headers: 0 files restamped. --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 598c76b..892f7b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "devDependencies": { "@cldmv/configs": "^1.2.1", - "@cldmv/fix-headers": "^2.1.2", + "@cldmv/fix-headers": "^2.1.4", "@cldmv/vitest-runner": "^1.2.0", "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.0", @@ -97,9 +97,9 @@ } }, "node_modules/@cldmv/fix-headers": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@cldmv/fix-headers/-/fix-headers-2.1.2.tgz", - "integrity": "sha512-1OnUKIkFRMJfJJ4ypyBHsb6mqbCV4q77M/JcYunuB4Ibbg1/6bXK1qCFev5+UWbhgtpPKmiy2VfPCHpf4P1ToQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@cldmv/fix-headers/-/fix-headers-2.1.4.tgz", + "integrity": "sha512-PvCKMztN9k+jOjEpMCANMaDIkNW7cs2qp5P73JVzResk1+DfqhoZVqT9jpTT8cUu+6OGszsNqj8/X0Q9IhfNtg==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 75b0803..b5b8587 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "sideEffects": false, "devDependencies": { "@cldmv/configs": "^1.2.1", - "@cldmv/fix-headers": "^2.1.2", + "@cldmv/fix-headers": "^2.1.4", "@cldmv/vitest-runner": "^1.2.0", "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.0", From db833ff6de23890738d62cfbc6d3ffdd39a3b0e6 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 20:42:15 -0700 Subject: [PATCH 12/15] docs: list the fix-headers 2.1.4 bump (#57) in the v1.2.5 notes --- docs/changelog/v1/v1.2.5.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog/v1/v1.2.5.md b/docs/changelog/v1/v1.2.5.md index 7da58b0..67790ee 100644 --- a/docs/changelog/v1/v1.2.5.md +++ b/docs/changelog/v1/v1.2.5.md @@ -57,6 +57,10 @@ The repository now includes the Apache-2.0 `LICENSE` file ([#54](https://github. - **NEW:** backfilled [v1.2.4](./v1.2.4.md), and corrected the [v1.2.3](./v1.2.3.md) release month to October 2026. - README restructured to the standard CLDMV layout, with a new Requirements section that states the `require()` Node.js floor. +## ๐Ÿ”ง Dependencies + +- `@cldmv/fix-headers` (dev dependency) `^2.1.2` โ†’ `^2.1.4` ([#57](https://github.com/CLDMV/uuid/pull/57)). Version 2.1.4 no longer writes a JavaScript comment into JSON or Markdown files, processes every repeated `--input`, and never walks dependency folders such as `node_modules`. Only `package.json` and the lockfile changed; no file headers were restamped and the published package is unaffected. + --- ## Upgrade notes From 4ecbcc1a40cade92fe02be374bf8ebd1c40ed283 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sun, 4 Oct 2026 19:03:44 -0700 Subject: [PATCH 13/15] deps: bump @cldmv/fix-headers to 2.2.0 Restamping the file headers under 2.2.0 changed no headers (0 files restamped). --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 892f7b8..11b1648 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "devDependencies": { "@cldmv/configs": "^1.2.1", - "@cldmv/fix-headers": "^2.1.4", + "@cldmv/fix-headers": "^2.2.0", "@cldmv/vitest-runner": "^1.2.0", "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.0", @@ -97,9 +97,9 @@ } }, "node_modules/@cldmv/fix-headers": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@cldmv/fix-headers/-/fix-headers-2.1.4.tgz", - "integrity": "sha512-PvCKMztN9k+jOjEpMCANMaDIkNW7cs2qp5P73JVzResk1+DfqhoZVqT9jpTT8cUu+6OGszsNqj8/X0Q9IhfNtg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cldmv/fix-headers/-/fix-headers-2.2.0.tgz", + "integrity": "sha512-EQTAKCo0B639q2bde+vO5ciYbtvNgAJFm0DBthIJQnmTFJmQDUlObp5EsTRgg5CUlFoUnGMX+q35LC02QvYU4w==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index b5b8587..c78632e 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "sideEffects": false, "devDependencies": { "@cldmv/configs": "^1.2.1", - "@cldmv/fix-headers": "^2.1.4", + "@cldmv/fix-headers": "^2.2.0", "@cldmv/vitest-runner": "^1.2.0", "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.0", From 9f2ae3f15d497278da47f2e94d8dd8bb25084efa Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sun, 4 Oct 2026 19:14:57 -0700 Subject: [PATCH 14/15] deps: bump @cldmv/configs to 1.2.4 Shared fix-headers config 1.2.4 no longer forces author updates. Restamping the file headers changed no headers (0 files restamped). --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11b1648..71b0261 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.2.5", "license": "Apache-2.0", "devDependencies": { - "@cldmv/configs": "^1.2.1", + "@cldmv/configs": "^1.2.4", "@cldmv/fix-headers": "^2.2.0", "@cldmv/vitest-runner": "^1.2.0", "@types/node": "^26.6.4", @@ -86,9 +86,9 @@ } }, "node_modules/@cldmv/configs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@cldmv/configs/-/configs-1.2.1.tgz", - "integrity": "sha512-R3GhDwdTqJwuRZ8/kVlUew0ezhZPxdFSJXky8jzfbzQEXDYQLZgPILSwzzKYHgRKu9P2ghXznzYKRPrsImiTNg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cldmv/configs/-/configs-1.2.4.tgz", + "integrity": "sha512-7HPqAgCKqol3fHpawEsXJ5ZqHxlDPZk1puoFu43f/gaNfhWwufDTzjkvLk90yVEumyz4N3pUwIxfbHUkUTpDEg==", "dev": true, "license": "Apache-2.0", "funding": { diff --git a/package.json b/package.json index c78632e..92466df 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ ], "sideEffects": false, "devDependencies": { - "@cldmv/configs": "^1.2.1", + "@cldmv/configs": "^1.2.4", "@cldmv/fix-headers": "^2.2.0", "@cldmv/vitest-runner": "^1.2.0", "@types/node": "^26.6.4", From 49297e684855cdb550b42c7b839e0a17c41507c2 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sun, 4 Oct 2026 20:09:01 -0700 Subject: [PATCH 15/15] docs: update the v1.2.5 release notes --- README.md | 1 + docs/changelog/v1/v1.2.5.md | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2fe4a8d..8b5050a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ The custom variants (`TA`, `TB`, `IA`) live in the variant `111` namespace, so t - **`require()` works**: the CommonJS entry failed to load in every earlier release, because the ESM entry it wraps used top-level `await`, which Node's synchronous `require(esm)` rejects with `ERR_REQUIRE_ASYNC_MODULE`. The entry no longer uses top-level `await`, so `require("@cldmv/uuid")` now returns the same `UUID` object as `import` on Node.js ^20.19.0 or >=22.12.0, and older versions get a clear error that points to `import()`. ESM behavior and the exported names are unchanged ([#50](https://github.com/CLDMV/uuid/pull/50)). - **Real TypeScript types**: `UUID` used to resolve to `any`. The package and its subpaths now ship accurate declarations, with optional generator options and `toBuffer()` typed as `Buffer` in Node projects and `Uint8Array` in browser and bundler projects ([#55](https://github.com/CLDMV/uuid/pull/55)). The repository also gains its Apache-2.0 `LICENSE` file ([#54](https://github.com/CLDMV/uuid/pull/54)). +- **Dev toolchain**: `@cldmv/fix-headers` 2.2.0 (`@Last modified by` now follows content edits only), `@cldmv/configs` 1.2.4 and a new `@types/node` dev dependency for the type tests, all dev-only; no file headers were restamped ([#55](https://github.com/CLDMV/uuid/pull/55), [#57](https://github.com/CLDMV/uuid/pull/57), [#59](https://github.com/CLDMV/uuid/pull/59)). - [View full v1.2.5 Changelog](https://github.com/CLDMV/uuid/blob/master/docs/changelog/v1/v1.2.5.md) ### Recent Releases diff --git a/docs/changelog/v1/v1.2.5.md b/docs/changelog/v1/v1.2.5.md index 67790ee..13a77a3 100644 --- a/docs/changelog/v1/v1.2.5.md +++ b/docs/changelog/v1/v1.2.5.md @@ -10,7 +10,7 @@ v1.2.5 makes `require("@cldmv/uuid")` work. The CommonJS entry has failed to load in every release up to and including v1.2.4, because the ESM entry it wraps used top-level `await`. This release removes the top-level `await`, so `require()` returns the same `UUID` object that `import` does on any Node.js version with synchronous `require(esm)`, and fails with a clear message on versions without it. -It also gives TypeScript users real types for the first time: `UUID` used to resolve to `any`, and it is now fully typed, with `Buffer` returns in Node projects and `Uint8Array` returns in browser and bundler projects. The repository finally carries the Apache-2.0 `LICENSE` file that `package.json` has always declared. +It also gives TypeScript users real types for the first time: `UUID` used to resolve to `any`, and it is now fully typed, with `Buffer` returns in Node projects and `Uint8Array` returns in browser and bundler projects. The repository finally carries the Apache-2.0 `LICENSE` file that `package.json` has always declared. The dev toolchain moves to `@cldmv/fix-headers` 2.2.0 and `@cldmv/configs` 1.2.4, and `@types/node` is added for the new type tests. ESM consumers see no change in runtime behavior. The exported names (`default`, `UUID`, `uuid`, `ISSUER_CATEGORIES`) and their values are the same as in v1.2.4. @@ -39,7 +39,7 @@ ESM consumers see no change in runtime behavior. The exported names (`default`, - The options argument of `v1`, `v4`, `v6`, `v7` and `v8` is optional and typed (`UUID.v4()` used to be a compile error once the types resolved). Passing `buf` returns that same buffer, typed as whatever you passed in; otherwise the methods return a `string`. - `TA`/`TB` timestamps and `entropy` arguments are optional, and `getRegistry()`'s `IssuerRegistry` type resolves. -- `toBuffer()` returns `Buffer` in Node projects (`module`/`moduleResolution` `node16`/`nodenext` with `@types/node`) and `Uint8Array` in browser and bundler projects, chosen through a private `#bytes-type` import. The package never forces Node's globals into a project: without `@types/node` the type falls back to `Uint8Array`. +- `toBuffer()` returns `Buffer` in Node projects (`module`/`moduleResolution` `node16`/`nodenext` with `@types/node`) and `Uint8Array` in browser and bundler projects, chosen through a private `#bytes-type` import. The package never forces Node's globals into a project: without `@types/node` the type falls back to `Uint8Array`. The two alias declarations behind it live in a new `typings/` folder (`typings/bytes.d.mts` and `typings/bytes-node.d.mts`), which is added to the package's `files` and selected through a new `imports` entry in `package.json`. A new `npm run test:types` check (run by `npm test` and `npm run coverage`) packs the package, installs it into a throwaway consumer and compiles it under `nodenext` and `bundler` resolution, including the README's TypeScript example. @@ -59,7 +59,12 @@ The repository now includes the Apache-2.0 `LICENSE` file ([#54](https://github. ## ๐Ÿ”ง Dependencies -- `@cldmv/fix-headers` (dev dependency) `^2.1.2` โ†’ `^2.1.4` ([#57](https://github.com/CLDMV/uuid/pull/57)). Version 2.1.4 no longer writes a JavaScript comment into JSON or Markdown files, processes every repeated `--input`, and never walks dependency folders such as `node_modules`. Only `package.json` and the lockfile changed; no file headers were restamped and the published package is unaffected. +All changes are dev-only and none ships in the package. The package still has no runtime dependencies. + +- `@cldmv/fix-headers` `^2.1.2` โ†’ `^2.2.0`, resolved to 2.2.0. The range was first raised to `^2.1.4` ([#57](https://github.com/CLDMV/uuid/pull/57)) and then to `^2.2.0` ([#59](https://github.com/CLDMV/uuid/pull/59)). Version 2.1.4 no longer writes a JavaScript comment into JSON or Markdown files, processes every repeated `--input`, and never walks dependency folders such as `node_modules`. Version 2.2.0 changes `@Last modified by` only when a file's content was edited, so header-only rewrites keep the recorded editor. +- `@cldmv/configs` `^1.2.1` โ†’ `^1.2.4`, resolved to 1.2.4. The shared fix-headers config now sets `forceAuthorUpdate` and `forceLastModifiedAuthorUpdate` to false ([#59](https://github.com/CLDMV/uuid/pull/59)). +- `@types/node` `^26.6.4` added (resolved to 26.6.4, with its transitive `undici-types` 8.9.0). The type-check suite uses it to prove that `toBuffer()` returns `Buffer` in a Node project ([#55](https://github.com/CLDMV/uuid/pull/55)). +- No file headers were restamped: the fix-headers and configs bump PRs change only `package.json` and the lockfile, and the published package is unaffected. ---