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. diff --git a/README.md b/README.md index 7a0f582..8b5050a 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,52 @@ # @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)). +- **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 +- **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 +58,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 +148,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 +185,9 @@ const buffer = uuid.toBuffer(); // 2 (milliseconds) ### Standard RFC UUID Examples -### Standard RFC UUID Examples - ```javascript import { UUID } from "@cldmv/uuid"; @@ -895,7 +929,9 @@ if (UUID.validateRFC(v4)) { } ``` -## Performance +--- + +## โšก Performance The library is optimized for high-performance UUID generation with collision resistance: @@ -907,7 +943,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 +986,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 +1015,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: @@ -982,9 +1025,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,12 +1041,16 @@ 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 +--- + +## ๐Ÿ“š 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 +1060,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 + +[![GitHub license]][github_license_url] [![npm license]][npm_license_url] -## Changelog +Apache-2.0 ยฉ Shinrai / CLDMV. See [LICENSE](https://github.com/CLDMV/uuid/blob/master/LICENSE) for the full text. -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 +1115,6 @@ See [docs/changelog/](https://github.com/CLDMV/uuid/tree/master/docs/changelog/) Made with โค๏ธ by [CLDMV](https://cldmv.net) - @@ -1064,14 +1122,28 @@ 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 +[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 [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.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. diff --git a/docs/changelog/v1/v1.2.5.md b/docs/changelog/v1/v1.2.5.md new file mode 100644 index 0000000..13a77a3 --- /dev/null +++ b/docs/changelog/v1/v1.2.5.md @@ -0,0 +1,75 @@ +# @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. + +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. + +--- + +## ๐Ÿ› 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. + +## ๐Ÿ”ท 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`. 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. + +--- + +## ๐Ÿ“„ 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. +- **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 + +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. + +--- + +## 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()`. 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..e3a333f 100644 --- a/index.mjs +++ b/index.mjs @@ -13,19 +13,24 @@ * */ -// 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; +export { UUID as default, UUID, UUID as uuid, ISSUER_CATEGORIES }; diff --git a/package-lock.json b/package-lock.json index 2ef5bc4..71b0261 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "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", - "@cldmv/fix-headers": "^2.1.2", + "@cldmv/configs": "^1.2.4", + "@cldmv/fix-headers": "^2.2.0", "@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" @@ -85,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": { @@ -96,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.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": { @@ -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 7fb92f4..92466df 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", @@ -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,11 +47,24 @@ "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" }, + "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" @@ -53,9 +75,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", + "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", + "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", @@ -111,13 +135,15 @@ "types/dist/", "types/index.d.mts", "types/index.d.mts.map", + "typings/", "dist/" ], "sideEffects": false, "devDependencies": { - "@cldmv/configs": "^1.2.1", - "@cldmv/fix-headers": "^2.1.2", + "@cldmv/configs": "^1.2.4", + "@cldmv/fix-headers": "^2.2.0", "@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..1723583 100644 --- a/src/uuid.mjs +++ b/src/uuid.mjs @@ -44,6 +44,42 @@ 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 + * @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 +89,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 +128,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 +162,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 +233,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 +242,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 +254,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 +266,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 +276,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 +327,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 +573,7 @@ class UUID { /** * Convert UUID to buffer - * @returns {Buffer} UUID as 16-byte buffer (Node); a Uint8Array copy 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); @@ -605,7 +646,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 +715,22 @@ 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` + * @template {Uint8Array} T + * @overload + * @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 + * @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 +747,22 @@ 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` + * @template {Uint8Array} T + * @overload + * @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 + * @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 +779,66 @@ 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` + * @template {Uint8Array} T + * @overload + * @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 + * @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` + * @template {Uint8Array} T + * @overload + * @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 + * @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` + * @template {Uint8Array} T + * @overload + * @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 + * @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 +854,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 +872,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 +901,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/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/tests/types/consumer.test.mjs b/tests/types/consumer.test.mjs new file mode 100644 index 0000000..7971680 --- /dev/null +++ b/tests/types/consumer.test.mjs @@ -0,0 +1,336 @@ +/** + * + * @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`); + + // 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, "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"; + +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"; + +const n: number = UUID.v4(); + +export { n }; +` + ); +}); + +after(() => { + rmSync(workDir, { recursive: true, force: true }); +}); + +/** 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 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); + 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"], []); + 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. + // 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); +}); + +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/index.d.mts b/types/index.d.mts index 5189d85..e778498 100644 --- a/types/index.d.mts +++ b/types/index.d.mts @@ -1,15 +1,4 @@ -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 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 6d2496d..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":""} \ 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 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..cd361ca 100644 --- a/types/src/uuid.d.mts +++ b/types/src/uuid.d.mts @@ -1,3 +1,81 @@ +/** + * 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. + */ +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; +}; +/** + * 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 + * @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 +84,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 +177,23 @@ 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` + * @template {Uint8Array} T + * @overload + * @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: T; + }): T; /** * Generate a version 3 (namespace with MD5) UUID * @param {string} name - Name to hash @@ -112,10 +203,23 @@ 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` + * @template {Uint8Array} T + * @overload + * @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: T; + }): T; /** * Generate a version 5 (namespace with SHA-1) UUID * @param {string} name - Name to hash @@ -125,22 +229,61 @@ 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` + * @template {Uint8Array} T + * @overload + * @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: T; + }): T; /** * 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` + * @template {Uint8Array} T + * @overload + * @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: T; + }): T; /** * 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` + * @template {Uint8Array} T + * @overload + * @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: T; + }): T; /** * Convert UUID string to byte array * @param {string} uuid - UUID string @@ -149,10 +292,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 +304,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 +312,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 +345,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 +435,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 {Bytes} UUID as a 16-byte copy: a Node Buffer (a Uint8Array subclass) in Node, a plain Uint8Array in environments without Buffer */ - toBuffer(): Buffer; + 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 a122b6e..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..d90adca --- /dev/null +++ b/typings/bytes-node.d.mts @@ -0,0 +1,27 @@ +/** + * + * @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`. + * + * 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). + */ +type NodeBuffer = typeof globalThis extends { Buffer: { alloc(size: number): infer B } } ? B : never; + +export type Bytes = [NodeBuffer] extends [never] ? Uint8Array : NodeBuffer; 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;