From a8c6f56f2ea05d48a730bfc780db8fcb79a4706f Mon Sep 17 00:00:00 2001 From: Hasko Date: Tue, 23 Jun 2026 17:07:51 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20=F0=9F=93=9D=20Fix=20VitePress=20build?= =?UTF-8?q?=20and=20preview=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pages.yml | 58 + .gitignore | 5 + .gitignore.docs | 3 + README.md | 322 +-- TECHNICAL_DOCUMENTATION.md | 1797 +---------------- docs/.vitepress/config.mts | 197 ++ docs/.vitepress/theme/custom.css | 27 + docs/.vitepress/theme/index.ts | 4 + docs/404.md | 10 + docs/getting-started/basic-usage.md | 59 + docs/getting-started/installation.md | 34 + docs/getting-started/introduction.md | 46 + docs/guides/agent-skill.md | 29 + docs/guides/custom-value-objects.md | 63 + docs/guides/error-handling.md | 60 + docs/guides/null-object.md | 37 + docs/guides/release-flow.md | 35 + docs/guides/serialization.md | 46 + docs/index.md | 50 + docs/public/logo.svg | 4 + .../reference/asymmetric-encrypted-payload.md | 56 + docs/reference/calendar-day.md | 72 + docs/reference/color.md | 62 + docs/reference/coordinates.md | 58 + docs/reference/crypto-notes.md | 85 + docs/reference/day-of-week.md | 65 + docs/reference/day.md | 54 + docs/reference/duration.md | 70 + docs/reference/email.md | 57 + docs/reference/encrypted-key-pair.md | 66 + docs/reference/encrypted-payload.md | 54 + docs/reference/encrypted-private-key.md | 67 + docs/reference/enum.md | 70 + docs/reference/hash.md | 54 + docs/reference/hour.md | 70 + docs/reference/index.md | 66 + docs/reference/integer.md | 57 + docs/reference/key-pair.md | 67 + docs/reference/key.md | 48 + docs/reference/latitude.md | 54 + docs/reference/longitude.md | 54 + docs/reference/md5-hash.md | 62 + docs/reference/media.md | 60 + docs/reference/month-of-year.md | 61 + docs/reference/month.md | 62 + docs/reference/null-object.md | 58 + docs/reference/number-value-object.md | 73 + docs/reference/password.md | 55 + docs/reference/positive-number.md | 58 + docs/reference/private-key.md | 68 + docs/reference/public-key.md | 66 + docs/reference/sha256-hash.md | 62 + docs/reference/sha512-hash.md | 62 + docs/reference/short-id.md | 64 + docs/reference/signature.md | 62 + docs/reference/string-value-object.md | 64 + docs/reference/symmetric-encrypted-payload.md | 56 + docs/reference/symmetric-key.md | 76 + docs/reference/timestamp-interval.md | 74 + docs/reference/timestamp.md | 89 + docs/reference/unique-object-array.md | 66 + docs/reference/uuid.md | 61 + docs/reference/value-object.md | 62 + docs/reference/year.md | 53 + package.json | 34 +- yarn.lock | 1171 ++++++++++- 66 files changed, 4827 insertions(+), 2044 deletions(-) create mode 100644 .github/workflows/pages.yml create mode 100644 .gitignore.docs create mode 100644 docs/.vitepress/config.mts create mode 100644 docs/.vitepress/theme/custom.css create mode 100644 docs/.vitepress/theme/index.ts create mode 100644 docs/404.md create mode 100644 docs/getting-started/basic-usage.md create mode 100644 docs/getting-started/installation.md create mode 100644 docs/getting-started/introduction.md create mode 100644 docs/guides/agent-skill.md create mode 100644 docs/guides/custom-value-objects.md create mode 100644 docs/guides/error-handling.md create mode 100644 docs/guides/null-object.md create mode 100644 docs/guides/release-flow.md create mode 100644 docs/guides/serialization.md create mode 100644 docs/index.md create mode 100644 docs/public/logo.svg create mode 100644 docs/reference/asymmetric-encrypted-payload.md create mode 100644 docs/reference/calendar-day.md create mode 100644 docs/reference/color.md create mode 100644 docs/reference/coordinates.md create mode 100644 docs/reference/crypto-notes.md create mode 100644 docs/reference/day-of-week.md create mode 100644 docs/reference/day.md create mode 100644 docs/reference/duration.md create mode 100644 docs/reference/email.md create mode 100644 docs/reference/encrypted-key-pair.md create mode 100644 docs/reference/encrypted-payload.md create mode 100644 docs/reference/encrypted-private-key.md create mode 100644 docs/reference/enum.md create mode 100644 docs/reference/hash.md create mode 100644 docs/reference/hour.md create mode 100644 docs/reference/index.md create mode 100644 docs/reference/integer.md create mode 100644 docs/reference/key-pair.md create mode 100644 docs/reference/key.md create mode 100644 docs/reference/latitude.md create mode 100644 docs/reference/longitude.md create mode 100644 docs/reference/md5-hash.md create mode 100644 docs/reference/media.md create mode 100644 docs/reference/month-of-year.md create mode 100644 docs/reference/month.md create mode 100644 docs/reference/null-object.md create mode 100644 docs/reference/number-value-object.md create mode 100644 docs/reference/password.md create mode 100644 docs/reference/positive-number.md create mode 100644 docs/reference/private-key.md create mode 100644 docs/reference/public-key.md create mode 100644 docs/reference/sha256-hash.md create mode 100644 docs/reference/sha512-hash.md create mode 100644 docs/reference/short-id.md create mode 100644 docs/reference/signature.md create mode 100644 docs/reference/string-value-object.md create mode 100644 docs/reference/symmetric-encrypted-payload.md create mode 100644 docs/reference/symmetric-key.md create mode 100644 docs/reference/timestamp-interval.md create mode 100644 docs/reference/timestamp.md create mode 100644 docs/reference/unique-object-array.md create mode 100644 docs/reference/uuid.md create mode 100644 docs/reference/value-object.md create mode 100644 docs/reference/year.md diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..0334ce3 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,58 @@ +name: Deploy documentation + +on: + push: + branches: [master] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Enable Corepack + run: corepack enable + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: 22 + cache: yarn + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build documentation + run: yarn docs:build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 5b5117c..4ecde48 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ /dist/ /tests/coverage/ .npmrc + +# VitePress +/docs/.vitepress/cache +/docs/.vitepress/dist +/docs/.vitepress/.temp diff --git a/.gitignore.docs b/.gitignore.docs new file mode 100644 index 0000000..dd37aa8 --- /dev/null +++ b/.gitignore.docs @@ -0,0 +1,3 @@ +# VitePress +/docs/.vitepress/cache +/docs/.vitepress/dist diff --git a/README.md b/README.md index ec515c9..8fafe4d 100644 --- a/README.md +++ b/README.md @@ -3,292 +3,110 @@ [![npm version](https://img.shields.io/npm/v/@haskou/value-objects.svg)](https://www.npmjs.com/package/@haskou/value-objects) [![license](https://img.shields.io/npm/l/@haskou/value-objects.svg)](LICENSE.txt) -# Value Objects +## Documentation -A TypeScript lightweight library for creating safe, immutable, -and validated **Value Objects**. Perfect for applications that require **Domain-Driven Design (DDD)** and **type safety**. +Full documentation is available at **https://haskou.github.io/value-objects/**. -## πŸš€ Quick Start +The documentation includes installation, quick start, examples, error handling, serialization notes, and one reference page per exported class. -```bash -npm install @haskou/value-objects -``` +Reusable agent instructions and engineering skills are available at **https://github.com/haskou/ddd-engineer-skills**. -```typescript -import { Email, PositiveNumber, Color, Hour } from '@haskou/value-objects'; +# Value Objects -// βœ… Automatic validation on construction -const email = new Email('user@example.com'); // Valid -const price = new PositiveNumber(29.99); // Valid -const color = new Color('#FF0000'); // Valid -const hour = new Hour('09:30'); // Valid +A TypeScript library for validated, immutable primitive wrappers and small utility value objects. -// ❌ Immediate errors with invalid values -const badEmail = new Email('not-an-email'); // Error! -const badPrice = new PositiveNumber(-5); // Error! -``` +It provides ready-to-use objects for strings, numbers, identifiers, dates, coordinates, hashes, media, collections, and cryptographic payload helpers. Validation happens when an object is created, so code receiving one of these objects can rely on its shape. -## πŸ€” Why use Value Objects? +## Installation -**Without Value Objects:** -```typescript -function createUser(email: string, age: number): void { - // Are they valid? We don't know until runtime - if (!email.includes('@')) throw new Error('Invalid email'); - if (age <= 0) throw new Error('Invalid age'); - // Creation stuff -} +```bash +npm install @haskou/value-objects ``` -**With Value Objects:** -```typescript -function createUser(email: Email, age: PositiveNumber): void { - // If we get here, values ARE valid - // Creation stuff -} +```bash +yarn add @haskou/value-objects ``` -This prevents defensive programming and if-else validations -since it implements Null Object pattern (see technical documentation). -Also value-objects can be modified easily keeping the same logic along -your application. - -## πŸ“¦ Available Value Objects - -### πŸ”€ Basic -- **`StringValueObject`** - Strings with length validation -- **`Password`** - Password strings with length and complexity validation -- **`NumberValueObject`** - Numbers with math operations -- **`Integer`** - Whole numbers -- **`PositiveNumber`** - Positive numbers (> 0) - -### πŸ†” Identifiers -- **`ShortId`** - MongoDB ObjectId strings (24‑character hex) -- **`UUID`** - Universally Unique Identifier (v4) - -### ✨ Specialized -- **`Email`** - Email addresses with automatic validation -- **`Color`** - Hex colors (#FF0000) - -### πŸ•’ Time -- **`Hour`** - Time in 24h format (09:30) -- **`Year`** - Years with leap year calculations -- **`CalendarDay`** - Calendar days -- **`Day`** - Days of month (1-31) -- **`DayOfWeek`** - Days of the week -- **`Month`** - Months (1-12) -- **`MonthOfYear`** - Month/year combinations -- **`Timestamp`** - Timestamps with operations -- **`TimestampInterval`** - Time intervals -- **`Duration`** - Duration in milliseconds - -### 🌍 Coordinates -- **`Latitude`** - Latitude (-90 to 90) -- **`Longitude`** - Longitude (-180 to 180) -- **`Coordinates`** - Coordinate pairs - -### πŸ” Cryptography -- **`KeyPair`** - Ed25519 key pair generation for signing and verification -- **`PrivateKey`** - Ed25519 private key (PEM format) for signing; also accepted by asymmetric payload decryption for data addressed to the matching key pair -- **`PublicKey`** - Ed25519 public key (PEM format) for signature verification; also accepted by asymmetric payload encryption to address data to the matching key pair -- **`Signature`** - Base64-encoded Ed25519 digital signature -- **`EncryptedPayload`** - Base container for dot-separated encrypted payload formats -- **`AsymmetricEncryptedPayload`** - Payload encrypted for an Ed25519/X25519 recipient key pair -- **`SymmetricEncryptedPayload`** - Payload encrypted with a symmetric AES-256-GCM key -- **`SymmetricKey`** - 32-byte AES-256-GCM key, randomly generated or deterministically derived from a `Password` and salt with scrypt -- **`EncryptedPrivateKey`** - Password-protected private key encryption using scrypt + AES-256-GCM -- **`EncryptedKeyPair`** - Key pair with encrypted private key - -Technical notes: - -- Asymmetric payload encryption is a classical hybrid scheme. It generates an ephemeral - X25519 key pair, converts the recipient Ed25519 key material to Montgomery - form for X25519 key agreement, derives a shared secret, then derives a - 256-bit AES key with HKDF-SHA256 and encrypts the payload with AES-256-GCM. -- The current asymmetric payload format is - `v2.x25519-hkdf-sha256-aes-256-gcm.ephemeralPublicKey.iv.cipherText.tag`, - with Base64-encoded key, IV, ciphertext, and tag fields. The previous - `ephemeralPublicKey.iv.cipherText.tag` format still decrypts for backward - compatibility. -- The v2 asymmetric header is authenticated as AES-GCM AAD. HKDF uses - `ephemeralPublicKey || recipientPublicKey` as salt and - `@haskou/value-objects/asymmetric-payload/v2` as domain-separated info. -- Symmetric payload encryption uses a 32-byte AES-256-GCM key directly. The - payload format is `v1.aes-256-gcm.iv.cipherText.tag`, with Base64-encoded - IV, ciphertext, and tag fields. -- `SymmetricKey.generate()` creates a random 256-bit key. - `SymmetricKey.fromPasswordUsingOwasp()` derives a 256-bit key with the OWASP - scrypt profile used by this package for new password-derived keys. - `SymmetricKey.fromPassword()` keeps its original defaults for backward - compatibility with existing encrypted data. -- A 32-byte key provides the AES-256 key size. Real security depends on using - either a high-entropy random key or a strong password with a unique, - non-empty salt. AES-GCM also requires that IVs do not repeat for the same key; - the library generates a random 96-bit IV for each encryption. -- Asymmetric payload encryption is intended for small payloads and is currently - capped at 1 MiB before encryption. Symmetric payload encryption is capped at - 8 MiB before encryption. -- New encrypted private keys use `v3.scrypt.N16384.r8.p5` with a 16-byte salt, - then AES-256-GCM with a 12-byte IV and 16-byte authentication tag. The older - v2 scrypt profile and legacy PBKDF2 format still decrypt, and - `needsReEncryption()` marks them for upgrade. The v3 header is authenticated - as AES-GCM AAD. -- This is not a post-quantum cryptography scheme. Ed25519 and X25519 are - classical elliptic-curve primitives, so neither signatures nor payload key - agreement are post-quantum secure. -- Payload encryption uses fresh ephemeral key material and a random IV per - encryption, but captured payloads can still be decrypted later if the - recipient private key is compromised. Treat it as recipient-addressed - encryption with ciphertext integrity, not as sender-authenticated encryption - or a forward-secret transport protocol. -- The payload encryption format is library-specific, not HPKE, and has not been - independently audited as a protocol. - -### πŸ“Ž Media -- **`Media`** - Binary/string content with Buffer, size, and Base64 helpers - -### #️ Hashes -- **`MD5Hash`** - MD5 hashes with helpers -- **`SHA256Hash`** - SHA‑256 hashes with helpers -- **`SHA512Hash`** - SHA‑512 hashes with helpers - -### πŸ“ Other -- **`Enum`** - Base class for typed enumerations -- **`UniqueObjectArray`** - Iterable collection that keeps comparable items unique - -## πŸ’‘ Basic Examples +## Quick start ```typescript -// Validated strings -const name = new StringValueObject('John Doe'); -const code = new StringValueObject('ABC', 3); // max 3 characters - -// Numbers with operations -const price = new NumberValueObject(29.99); -const discount = new NumberValueObject(5.00); -const total = price.subtract(discount); // 24.99 +import { Color, Email, Hour, PositiveNumber } from '@haskou/value-objects'; -// Emails const email = new Email('user@example.com'); -console.log(email.getDomain()); // 'example.com' +const price = new PositiveNumber(29.99); +const color = new Color('#FF0000'); +const hour = new Hour('09:30'); -// Colors -const red = new Color('#FF0000'); -const blue = Color.BLUE; // Predefined color +console.log(email.valueOf()); // 'user@example.com' +console.log(price.valueOf()); // 29.99 +console.log(color.toString()); // '#FF0000' +console.log(hour.addMinutes(45).toString()); // '10:15' +``` -// Coordinates -const latitude = new Latitude(40.7128); // New York -const longitude = new Longitude(-74.0060); -const coords = new Coordinates(latitude, longitude); +Invalid values throw specific errors. -// Time -const hour = new Hour('09:30'); -const year = new Year(2024); -console.log(year.isLeapYear()); // true - -// IDs -const id = ShortId.generate(); // 69ad70897364ee0d1406b1d0 -const uuid = UUID.generate(); // 3fd4c04a-8e73-4e10-aef3-f491b32ec538 - -// Hashes -const md5 = MD5Hash.from('hello'); // 5d41402abc4b2a76b9719d911017c592 -const sha256 = SHA256Hash.from('hello'); // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 -const sha512 = SHA512Hash.from('hello'); // 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043 -console.log(md5.toBase64()); - -// Crypto -const keyPair = await KeyPair.generate(); // Ed25519 key pair -const signature = keyPair.sign('hello world'); // Sign a message -keyPair.isValidSignature('hello world', signature); // true - -// Encrypted key pairs -const cryptoPassword = new Password('Secure-password-123!'); -const encrypted = await keyPair.encryptKeyPair(cryptoPassword); -const sig = await encrypted.sign('message', cryptoPassword); -encrypted.isValidSignature('message', sig); // true - -// Symmetric payload encryption -const symmetricKey = SymmetricKey.generate(); -const symmetricPayload = symmetricKey.encrypt('secret data'); -const plaintext = symmetricKey.decrypt(symmetricPayload); -console.log(plaintext.toString()); // 'secret data' - -// Deterministic key derivation from a password and explicit salt -const derivedKey = await SymmetricKey.fromPasswordUsingOwasp( - cryptoPassword, - { salt: 'stable-application-salt' }, -); - -// Media -const media = new Media('hello world'); -console.log(media.getSize()); // 11 -console.log(media.getBase64()); // 'aGVsbG8gd29ybGQ=' -console.log(media.getBuffer()); // - -// Unique collections -const weekdays = UniqueObjectArray.fromArray([ - DayOfWeek.MONDAY, - DayOfWeek.TUESDAY, - DayOfWeek.MONDAY, -]); -console.log(weekdays.length()); // 2 -console.log(weekdays.toArray().map((day) => day.toString())); // ['monday', 'tuesday'] +```typescript +import { Email, InvalidEmailError } from '@haskou/value-objects'; + +try { + new Email('not-an-email'); +} catch (error) { + if (error instanceof InvalidEmailError) { + console.error('Invalid email'); + } +} ``` -## πŸ“š Technical Documentation - -Need more details? Check the complete documentation: +## Available categories -**[πŸ“– TECHNICAL_DOCUMENTATION.md](./TECHNICAL_DOCUMENTATION.md)** +| Category | Examples | +| --- | --- | +| Base | `ValueObject`, `NullObject`, `Enum` | +| Strings | `StringValueObject`, `Password`, `Email`, `Color` | +| Numbers | `NumberValueObject`, `Integer`, `PositiveNumber` | +| IDs | `ShortId`, `UUID` | +| Time | `Timestamp`, `CalendarDay`, `Hour`, `Duration`, `MonthOfYear` | +| Coordinates | `Latitude`, `Longitude`, `Coordinates` | +| Hashes | `MD5Hash`, `SHA256Hash`, `SHA512Hash` | +| Media | `Media` | +| Collections | `UniqueObjectArray` | +| Crypto helpers | `KeyPair`, `PrivateKey`, `PublicKey`, `SymmetricKey`, encrypted payload objects | -Includes: -- βœ… Complete API for all Value Objects -- βœ… Advanced usage examples -- βœ… Error handling -- βœ… Design principles (immutability, null safety, etc.) -- βœ… Composition and extensibility patterns +See the complete API reference in the documentation: https://haskou.github.io/value-objects/reference/ -## πŸ› οΈ Development +## Documentation development ```bash -# Install dependencies -npm install +# Start local docs server +yarn docs:dev -# Run tests -npm test +# Build static documentation +yarn docs:build -# Build -npm run build +# Preview the built site +yarn docs:preview ``` -## 🌿 Release Branches - -Publishing is handled by CI when a pull request is merged into the default -branch (`master`, or `main` after a branch rename). Use these branch prefixes -to choose the npm version bump: +## Package development -- `fix/*` - patch release -- `feat/*` - minor release -- `break/*` - major release - -Branches without one of these prefixes still run CI, but they do not publish -to npm. Publishing uses npm Trusted Publishing from the `ci.yml` workflow. -After a successful publish, CI commits the release version to the default -branch, creates the matching `vX.Y.Z` Git tag, and publishes a compact GitHub -Release with the PR title, npm package link, release branch, and PR notes. +```bash +yarn install +yarn test +yarn build +``` -## 🀝 Contributing +## Release branches -1. Fork the repository -2. Create a branch: `git checkout -b feat/my-feature` -3. Make your changes and add tests -4. Run tests: `npm test` -5. Submit a pull request +Publishing is handled by CI when a pull request is merged into the default branch. -## πŸ“„ License +| Branch prefix | npm bump | +| --- | --- | +| `fix/*` | Patch | +| `feat/*` | Minor | +| `break/*` | Major | -MIT License - see the [LICENSE](LICENSE.txt) file for details. +Branches without one of these prefixes still run CI, but they do not publish to npm. ---- +## License -**Made with ❀️ and TypeScript** +MIT. See [LICENSE.txt](LICENSE.txt). diff --git a/TECHNICAL_DOCUMENTATION.md b/TECHNICAL_DOCUMENTATION.md index 4f957c8..0664a55 100644 --- a/TECHNICAL_DOCUMENTATION.md +++ b/TECHNICAL_DOCUMENTATION.md @@ -1,1796 +1,21 @@ -# Technical Documentation +# Technical documentation -Comprehensive technical documentation for the Value Objects library. +The maintained documentation lives in `docs/` and is built with VitePress. -## πŸ“‹ Table of Contents +Local development: -- [API Documentation](#api-documentation) - - [Base Classes](#base-classes) - - [String Value Objects](#string-value-objects) - - [Password Value Objects](#password-value-objects) - - [Number Value Objects](#number-value-objects) - - [Integer Value Objects](#integer-value-objects) - - [PositiveNumber Value Objects](#positivenumber-value-objects) - - [Year Value Objects](#year-value-objects) - - [Color Value Objects](#color-value-objects) - - [Email Value Objects](#email-value-objects) - - [ID Value Objects](#id-value-objects) - - [Hash Value Objects](#hash-value-objects) - - [Cryptography Value Objects](#cryptography-value-objects) - - [Encrypting and Decrypting Payloads](#encrypting-and-decrypting-payloads) - - [Symmetric Payload Encryption](#symmetric-payload-encryption) - - [Media Value Objects](#media-value-objects) - - [Collection Utilities](#collection-utilities) - - [Hour Value Objects](#hour-value-objects) - - [Time Value Objects](#time-value-objects) - - [Enum Value Objects](#enum-value-objects) -- [Error Handling](#error-handling) -- [Design Principles](#design-principles) - -## πŸ“š API Documentation - -### Base Classes - -#### ValueObject\ - -The abstract base class for all value objects. - -```typescript -abstract class ValueObject { - constructor(protected readonly value: T); - - public isEqual(other: unknown): boolean; - public valueOf(): T; - public toString(): string; - protected clone(value: T): this; -} -``` - -**Methods:** -- `valueOf()`: Returns the primitive value -- `toString()`: Returns string representation -- `isEqual(other)`: Compares equality with another value -- `clone(value)`: Creates a new instance with the given value - -### String Value Objects - -#### StringValueObject - -Represents immutable string values with length validation. - -```typescript -class StringValueObject extends ValueObject { - constructor(value: string | StringValueObject, maxLength?: number); - - public toString(): string; - public isEmpty(): boolean; -} -``` - -**Example:** -```typescript -// Basic usage -const username = new StringValueObject('alice123'); - -// With length limit -const shortCode = new StringValueObject('ABC', 3); - -// From another StringValueObject -const copy = new StringValueObject(username); - -// Validation -try { - new StringValueObject('a'.repeat(600)); // Throws InvalidStringLengthError -} catch (error) { - console.error('String too long'); -} -``` - -### Password Value Objects - -#### Password - -Represents immutable password strings with basic length and complexity validation. A `Password` must be between 12 and 256 characters long and include at least one uppercase letter, one lowercase letter, one number, and one symbol. This value object validates password shape only; it does not hash, store, or encrypt passwords by itself. - -```typescript -class Password extends StringValueObject { - constructor(value: string | StringValueObject); -} -``` - -**Example:** -```typescript -const password = new Password('Secure-password-123!'); - -try { - new Password('weak-password'); // Throws InvalidPasswordError -} catch (error) { - console.error('Invalid password'); -} -``` - -### Number Value Objects - -#### NumberValueObject - -Represents immutable numeric values with arithmetic operations. - -```typescript -class NumberValueObject extends ValueObject { - constructor(value: number | NumberValueObject); - - // Comparison methods - public isZero(): boolean; - public isGreaterThan(other: number | NumberValueObject): boolean; - public isGreaterOrEqualThan(other: number | NumberValueObject): boolean; - public isLessThan(other: number | NumberValueObject): boolean; - public isLessOrEqualThan(other: number | NumberValueObject): boolean; - - // Arithmetic operations - public add(other: number | NumberValueObject): NumberValueObject; - public subtract(other: number | NumberValueObject): NumberValueObject; - public multiply(other: number | NumberValueObject): NumberValueObject; - public divide(other: number | NumberValueObject): NumberValueObject; -} -``` - -**Example:** -```typescript -const a = new NumberValueObject(10); -const b = new NumberValueObject(5); - -// Arithmetic operations (immutable) -const sum = a.add(b); // 15 -const diff = a.subtract(b); // 5 -const prod = a.multiply(b); // 50 -const quot = a.divide(b); // 2 - -// Comparisons -console.log(a.isGreaterThan(b)); // true -console.log(b.isLessThan(a)); // true -console.log(a.isEqual(10)); // true - -// Works with primitive numbers -const result = a.add(3); // 13 - -// Original values remain unchanged -console.log(a.valueOf()); // 10 (unchanged) -console.log(b.valueOf()); // 5 (unchanged) -``` - -### Integer Value Objects - -#### Integer - -Represents immutable integer values (whole numbers) with all arithmetic operations inherited from NumberValueObject. - -```typescript -class Integer extends NumberValueObject { - constructor(value: number | NumberValueObject); - - // Inherits all NumberValueObject methods: - // Comparison methods - public isZero(): boolean; - public isGreaterThan(other: number | NumberValueObject): boolean; - public isGreaterOrEqualThan(other: number | NumberValueObject): boolean; - public isLessThan(other: number | NumberValueObject): boolean; - public isLessOrEqualThan(other: number | NumberValueObject): boolean; - - // Arithmetic operations - public add(other: number | NumberValueObject): NumberValueObject; - public subtract(other: number | NumberValueObject): NumberValueObject; - public multiply(other: number | NumberValueObject): NumberValueObject; - public divide(other: number | NumberValueObject): NumberValueObject; -} -``` - -**Example:** -```typescript -// Valid integers -const count = new Integer(42); -const negative = new Integer(-10); -const zero = new Integer(0); - -// Arithmetic operations (inherited from NumberValueObject) -const sum = count.add(negative); // 32 -const product = count.multiply(2); // 84 -const quotient = count.divide(2); // 21 - -// Comparisons -console.log(count.isGreaterThan(zero)); // true -console.log(negative.isLessThan(zero)); // true -console.log(zero.isZero()); // true - -// From another NumberValueObject -const numberValue = new NumberValueObject(15); -const integerFromNumber = new Integer(numberValue); -console.log(integerFromNumber.valueOf()); // 15 - -// String representation -console.log(count.toString()); // '42' -console.log(negative.valueOf()); // -10 - -// Note: Arithmetic operations return NumberValueObject, not Integer -// This is because operations might result in non-integer values -const division = count.divide(3); // Returns NumberValueObject with value 14 - -// Validation -try { - new Integer(42.5); // Throws InvalidIntegerError - new Integer(3.14159); // Throws InvalidIntegerError - new Integer(Infinity); // Throws InvalidIntegerError - new Integer(-Infinity); // Throws InvalidIntegerError - new Integer(NaN); // Throws InvalidNumberError -} catch (error) { - console.error('Value must be a valid integer'); -} - -// Works with large integers -const large = new Integer(1000000); // Valid -const veryLarge = new Integer(-999999); // Valid -``` - -### PositiveNumber Value Objects - -#### PositiveNumber - -Represents immutable positive numeric values (greater than 0) with all arithmetic operations inherited from NumberValueObject. - -```typescript -class PositiveNumber extends NumberValueObject { - constructor(value: number | NumberValueObject); - - // Inherits all NumberValueObject methods: - // Comparison methods - public isZero(): boolean; // Always returns false - public isGreaterThan(other: number | NumberValueObject): boolean; - public isGreaterOrEqualThan(other: number | NumberValueObject): boolean; - public isLessThan(other: number | NumberValueObject): boolean; - public isLessOrEqualThan(other: number | NumberValueObject): boolean; - - // Arithmetic operations - public add(other: number | NumberValueObject): NumberValueObject; - public subtract(other: number | NumberValueObject): NumberValueObject; - public multiply(other: number | NumberValueObject): NumberValueObject; - public divide(other: number | NumberValueObject): NumberValueObject; -} -``` - -**Example:** -```typescript -// Valid positive numbers -const quantity = new PositiveNumber(5); -const price = new PositiveNumber(19.99); -const percentage = new PositiveNumber(0.15); - -// Arithmetic operations (inherited from NumberValueObject) -const total = quantity.multiply(price); // 99.95 -const increased = price.add(10); // 29.99 -const half = quantity.divide(2); // 2.5 - -// Comparisons -console.log(price.isGreaterThan(quantity)); // true -console.log(quantity.isLessThan(price)); // true -console.log(quantity.isZero()); // false (always false for PositiveNumber) - -// From another PositiveNumber -const copy = new PositiveNumber(quantity); -console.log(copy.valueOf()); // 5 - -// String representation -console.log(quantity.toString()); // '5' -console.log(price.valueOf()); // 19.99 - -// Note: Arithmetic operations return NumberValueObject, not PositiveNumber -// This is because operations might result in non-positive values -const result = quantity.subtract(10); // Returns NumberValueObject with value -5 - -// Validation -try { - new PositiveNumber(0); // Throws InvalidPositiveNumberError - new PositiveNumber(-5); // Throws InvalidPositiveNumberError - new PositiveNumber(-0.1); // Throws InvalidPositiveNumberError -} catch (error) { - console.error('Value must be greater than 0'); -} - -// Works with decimals -const decimal = new PositiveNumber(0.001); // Valid -const large = new PositiveNumber(1000000); // Valid -``` - -### Year Value Objects - -#### Year - -Represents immutable year values with leap year calculations and date utilities. Inherits all functionality from Integer. - -```typescript -class Year extends Integer { - constructor(value: number | NumberValueObject); - - // Year-specific methods - public isLeapYear(): boolean; - public getNumberOfDays(): number; - - // Inherits all Integer and NumberValueObject methods: - // Comparison methods - public isZero(): boolean; - public isGreaterThan(other: number | NumberValueObject): boolean; - public isGreaterOrEqualThan(other: number | NumberValueObject): boolean; - public isLessThan(other: number | NumberValueObject): boolean; - public isLessOrEqualThan(other: number | NumberValueObject): boolean; - - // Arithmetic operations - public add(other: number | NumberValueObject): NumberValueObject; - public subtract(other: number | NumberValueObject): NumberValueObject; - public multiply(other: number | NumberValueObject): NumberValueObject; - public divide(other: number | NumberValueObject): NumberValueObject; -} -``` - -**Example:** -```typescript -// Valid years -const currentYear = new Year(2024); -const pastYear = new Year(1900); -const futureYear = new Year(2100); -const year2000 = new Year(2000); - -// Leap year calculations -console.log(currentYear.isLeapYear()); // true (2024 is divisible by 4) -console.log(pastYear.isLeapYear()); // false (1900 is divisible by 100 but not 400) -console.log(futureYear.isLeapYear()); // false (2100 is divisible by 100 but not 400) -console.log(year2000.isLeapYear()); // true (2000 is divisible by 400) - -// Number of days in year -console.log(currentYear.getNumberOfDays()); // 366 (leap year) -console.log(pastYear.getNumberOfDays()); // 365 (regular year) -console.log(year2000.getNumberOfDays()); // 366 (leap year) - -// Arithmetic operations (inherited from NumberValueObject) -const nextYear = currentYear.add(1); // 2025 (as NumberValueObject) -const decade = currentYear.subtract(10); // 2014 (as NumberValueObject) - -// Comparisons -console.log(currentYear.isGreaterThan(pastYear)); // true -console.log(pastYear.isLessThan(futureYear)); // true -console.log(currentYear.isEqual(2024)); // true - -// From another NumberValueObject or Integer -const yearFromNumber = new NumberValueObject(2023); -const year = new Year(yearFromNumber); -console.log(year.valueOf()); // 2023 -console.log(year.isLeapYear()); // false - -// String representation -console.log(currentYear.toString()); // '2024' -console.log(pastYear.valueOf()); // 1900 - -// Common leap year patterns -const leapYears = [2020, 2024, 2028, 2032].map(y => new Year(y)); -const regularYears = [2021, 2022, 2023, 2025].map(y => new Year(y)); - -leapYears.forEach(year => { - console.log(`${year.valueOf()}: ${year.getNumberOfDays()} days`); // All show 366 -}); - -regularYears.forEach(year => { - console.log(`${year.valueOf()}: ${year.getNumberOfDays()} days`); // All show 365 -}); - -// Validation (inherits Integer validation) -try { - new Year(2024.5); // Throws InvalidIntegerError - new Year(NaN); // Throws InvalidNumberError - new Year(Infinity); // Throws InvalidIntegerError -} catch (error) { - console.error('Year must be a valid integer'); -} - -// Works with negative years (BCE) -const ancientYear = new Year(-500); // 500 BCE -console.log(ancientYear.valueOf()); // -500 -console.log(ancientYear.isLeapYear()); // false (year -500 is not a leap year) -``` - -### Color Value Objects - -#### Color - -Represents immutable hex color values with predefined colors. - -```typescript -class Color extends StringValueObject { - constructor(value: string | StringValueObject); - - public isEqual(other: Color): boolean; // Case-insensitive comparison - - // Predefined static colors - static readonly RED: Color; - static readonly GREEN: Color; - static readonly BLUE: Color; - static readonly BLACK: Color; - static readonly WHITE: Color; - static readonly YELLOW: Color; - static readonly CYAN: Color; - static readonly MAGENTA: Color; - static readonly ORANGE: Color; - static readonly PURPLE: Color; - static readonly PINK: Color; - static readonly BROWN: Color; -} -``` - -**Example:** -```typescript -// Valid hex colors -const red = new Color('#FF0000'); -const blue = new Color('#00F'); -const green = Color.GREEN; - -// Case-insensitive comparison -const color1 = new Color('#ff0000'); -const color2 = new Color('#FF0000'); -console.log(color1.isEqual(color2)); // true - -// From StringValueObject -const colorString = new StringValueObject('#ABCDEF'); -const color = new Color(colorString); - -// Validation -try { - new Color('invalid-color'); // Throws InvalidColorError -} catch (error) { - console.error('Invalid hex color format'); -} -``` - -### Email Value Objects - -#### Email - -Represents immutable email addresses with comprehensive validation. - -```typescript -class Email extends StringValueObject { - constructor(value: string | StringValueObject); -} +```bash +yarn docs:dev ``` -**Example:** -```typescript -// Valid email addresses -const userEmail = new Email('user@example.com'); -const workEmail = new Email('john.doe@company.org'); -const taggedEmail = new Email('user+newsletter@domain.co.uk'); +Production build: -// From StringValueObject -const emailString = new StringValueObject('admin@system.net'); -const email = new Email(emailString); - -// String representation -console.log(userEmail.toString()); // 'user@example.com' -console.log(userEmail.valueOf()); // 'user@example.com' - -// Equality comparison -const email1 = new Email('test@example.com'); -const email2 = new Email('test@example.com'); -console.log(email1.isEqual(email2)); // true -console.log(email1.isEqual('test@example.com')); // true - -// Inherits all StringValueObject methods -console.log(userEmail.isEmpty()); // false - -// Validation examples -try { - new Email('invalid-email'); // Throws InvalidEmailError - new Email('user@'); // Throws InvalidEmailError - new Email('user@domain'); // Throws InvalidEmailError - new Email('user@@domain.com'); // Throws InvalidEmailError -} catch (error) { - console.error('Invalid email format'); -} - -// Supports various valid formats -const validEmails = [ - 'simple@example.com', - 'user.name@domain.org', - 'user+tag@example.co.uk', - 'first.last@subdomain.company.travel', - 'admin123@my-domain.museum' -]; -``` - -### ID Value Objects - -#### ShortId - -Represents immutable MongoDB ObjectId values with automatic generation and validation. - -```typescript -class ShortId extends ValueObject { - public static generate(): ShortId; - constructor(value: string | StringValueObject); -} -``` - -**Example:** -```typescript -// Generate a new ObjectId -const id = ShortId.generate(); -console.log(id.toString()); // '507f1f77bcf86cd799439011' (24-character hex string) - -// Create from existing ObjectId string -const existingId = new ShortId('507f1f77bcf86cd799439011'); - -// From StringValueObject -const idString = new StringValueObject('507f1f77bcf86cd799439011'); -const idFromString = new ShortId(idString); - -// String representation -console.log(existingId.toString()); // '507f1f77bcf86cd799439011' -console.log(existingId.valueOf()); // '507f1f77bcf86cd799439011' - -// Equality comparison -const id1 = new ShortId('507f1f77bcf86cd799439011'); -const id2 = new ShortId('507f1f77bcf86cd799439011'); -console.log(id1.isEqual(id2)); // true - -// Validation -try { - new ShortId('invalid-id'); // Throws InvalidFormatError - new ShortId('short'); // Throws InvalidLengthError -} catch (error) { - console.error('Invalid ObjectId format'); -} -``` - -#### UUID - -Represents immutable UUID (Universally Unique Identifier) values with automatic generation and validation. - -```typescript -class UUID extends ValueObject { - public static generate(): UUID; - constructor(value: string | StringValueObject); -} -``` - -**Example:** -```typescript -// Generate a new UUID v4 -const uuid = UUID.generate(); -console.log(uuid.toString()); // '550e8400-e29b-41d4-a716-446655440000' (36-character string) - -// Create from existing UUID string -const existingUuid = new UUID('550e8400-e29b-41d4-a716-446655440000'); - -// From StringValueObject -const uuidString = new StringValueObject('550e8400-e29b-41d4-a716-446655440000'); -const uuidFromString = new UUID(uuidString); - -// String representation -console.log(existingUuid.toString()); // '550e8400-e29b-41d4-a716-446655440000' -console.log(existingUuid.valueOf()); // '550e8400-e29b-41d4-a716-446655440000' - -// Equality comparison -const uuid1 = new UUID('550e8400-e29b-41d4-a716-446655440000'); -const uuid2 = new UUID('550e8400-e29b-41d4-a716-446655440000'); -console.log(uuid1.isEqual(uuid2)); // true - -// Validation -try { - new UUID('invalid-uuid'); // Throws InvalidFormatError - new UUID('short-uuid'); // Throws InvalidLengthError -} catch (error) { - console.error('Invalid UUID format'); -} +```bash +yarn docs:build ``` -### Hash Value Objects - -#### MD5Hash -Represents immutable MD5 hashes with validation and utility methods. - -```typescript -class MD5Hash extends Hash { - public static isValid(hash: string | StringValueObject): boolean; - public static from(buffer: Buffer | string | StringValueObject): MD5Hash; - constructor(source: string | StringValueObject); -} -``` - -**Example:** -```typescript -// Compute an MD5 hash from a string or buffer -const md5 = MD5Hash.from('hello'); -console.log(md5.toString()); // '5d41402abc4b2a76b9719d911017c592' -console.log(md5.toBase64()); // 'XUEQKvLG6avlckeQEAXGSw==' - -// Create from existing hash string -const existing = new MD5Hash('5d41402abc4b2a76b9719d911017c592'); - -// Validation -try { - new MD5Hash('invalid'); // Throws InvalidHashError -} catch (err) { - console.error('Invalid MD5 hash'); -} -``` - -#### SHA256Hash -Represents immutable SHA‑256 hashes with validation and utility methods. - -```typescript -class SHA256Hash extends ValueObject { - public static isValid(hash: string | StringValueObject): boolean; - public static from(buffer: Buffer | string | StringValueObject): SHA256Hash; - constructor(source: string | StringValueObject); -} -``` - -**Example:** -```typescript -const sha256 = SHA256Hash.from('hello'); -console.log(sha256.toString().length); // 64 -console.log(sha256.toBase64()); - -try { - new SHA256Hash('abc'); // Throws InvalidHashError -} catch (err) { - console.error('Invalid SHA256 hash'); -} -``` - -#### SHA512Hash -Represents immutable SHA‑512 hashes with validation and utility methods. - -```typescript -class SHA512Hash extends ValueObject { - public static isValid(hash: string | StringValueObject): boolean; - public static from(buffer: Buffer | string | StringValueObject): SHA512Hash; - constructor(source: string | StringValueObject); -} -``` - -**Example:** -```typescript -const sha512 = SHA512Hash.from('hello'); -console.log(sha512.toString().length); // 128 -console.log(sha512.toBase64()); - -try { - new SHA512Hash('abc'); // Throws InvalidHashError -} catch (err) { - console.error('Invalid SHA512 hash'); -} -``` - -### Cryptography Value Objects - -Cryptographic key value objects use **Ed25519** elliptic curve keys in PEM format for signing and verification. Asymmetric payload encryption uses a library-specific hybrid public-key encryption scheme: Ed25519 key material is converted to X25519 (Montgomery form via `@noble/curves`), an ephemeral X25519 shared secret is expanded with HKDF-SHA256 to derive a 256-bit AES key, and the payload is encrypted with AES-256-GCM. The current asymmetric format authenticates its version and algorithm header as AES-GCM AAD, and uses HKDF salt/info values for domain separation. Symmetric payload encryption uses a caller-held 32-byte AES-256-GCM key directly. These formats are not HPKE, are not post-quantum cryptography schemes, and are not documented here as independently audited protocols. - -#### Key - -Abstract base class for cryptographic keys. Extends `ValueObject`. - -```typescript -abstract class Key extends ValueObject {} -``` - -#### PrivateKey - -Represents an immutable Ed25519 private key in PEM (PKCS8) format. Used for signing messages; also accepted by asymmetric payload decryption for data addressed to the matching key pair. +Published documentation: -```typescript -class PrivateKey extends Key { - public static fromPEM(pem: string | StringValueObject): PrivateKey; - constructor(value: string | StringValueObject); - public sign(payload: CryptoPayload): Signature; - public decrypt(encryptedPayload: EncryptedPayload): Buffer; -} -``` - -**Example:** -```typescript -// Create from PEM string -const privateKey = new PrivateKey(pemString); -const sameKey = PrivateKey.fromPEM(pemString); - -// Sign a message -const signature = privateKey.sign('hello world'); -console.log(signature.valueOf()); // Base64-encoded 88-character signature - -// Decrypt a payload encrypted with the corresponding public key -const decrypted = privateKey.decrypt(encryptedPayload); -console.log(decrypted.toString()); // Original plaintext -``` - -#### PublicKey - -Represents an immutable Ed25519 public key in PEM (SPKI) format. Used for verifying signatures; also accepted by asymmetric payload encryption to address data to the matching key pair. - -```typescript -class PublicKey extends Key { - public static fromPEM(pem: string | StringValueObject): PublicKey; - constructor(value: string | StringValueObject); - public isValidSignature(payload: CryptoPayload, signature: Signature): boolean; - public encrypt(payload: CryptoPayload): AsymmetricEncryptedPayload; -} -``` - -**Example:** -```typescript -// Create from PEM string -const publicKey = new PublicKey(pemString); - -// Verify a signature -const valid = publicKey.isValidSignature('hello world', signature); -console.log(valid); // true - -// Encrypt a payload for the corresponding private key -const encrypted = publicKey.encrypt('sensitive data'); -console.log(encrypted.valueOf()); // 'v2.x25519-hkdf-sha256-aes-256-gcm.ephemeralPub.iv.cipherText.tag' -``` - -#### Signature - -Represents an immutable Ed25519 digital signature as a base64-encoded string (88 characters). - -```typescript -class Signature extends ValueObject { - public static fromBuffer(buffer: Buffer): Signature; - constructor(value: string | StringValueObject); -} -``` - -**Example:** -```typescript -// Created by PrivateKey.sign() -const signature = privateKey.sign('data'); - -// Or from a raw buffer -const sigFromBuffer = Signature.fromBuffer(signatureBuffer); - -// Validation -try { - new Signature('too-short'); // Throws InvalidSignatureError -} catch (error) { - console.error('Signature must be 88 characters (base64)'); -} -``` - -#### KeyPair - -Manages a public/private Ed25519 key pair with generation, signing, verification, payload encryption/decryption, and serialization. - -```typescript -class KeyPair { - public static async generate(): Promise; - public static fromPrimitives(primitives: PrimitiveOf): KeyPair; - constructor(publicKey: PublicKey, privateKey: PrivateKey); - public sign(payload: CryptoPayload): Signature; - public isValidSignature(payload: CryptoPayload, signature: Signature): boolean; - public encrypt(payload: CryptoPayload): EncryptedPayload; - public decrypt(encryptedPayload: EncryptedPayload): Buffer; - public encryptKeyPair(password: CryptoPassword): Promise; - public toPrimitives(): { publicKey: string; privateKey: string }; -} -``` - -**Example:** -```typescript -// Generate a new ed25519 key pair -const keyPair = await KeyPair.generate(); - -// Sign and verify -const signature = keyPair.sign('important message'); -console.log(keyPair.isValidSignature('important message', signature)); // true - -// Encrypt and decrypt payloads -const encrypted = keyPair.encrypt('secret data'); -const decrypted = keyPair.decrypt(encrypted); -console.log(decrypted.toString()); // 'secret data' - -// Serialize and restore -const primitives = keyPair.toPrimitives(); -const restored = KeyPair.fromPrimitives(primitives); - -// Encrypt the private key with a validated password -const password = new Password('Secure-password-123!'); -const encryptedKeyPair = await keyPair.encryptKeyPair(password); -``` - -#### SymmetricKey - -Represents an immutable 32-byte AES-256-GCM key encoded as Base64. Keys can be generated randomly, loaded from a 32-byte buffer, loaded from Base64, or deterministically derived from a password plus an explicit salt with scrypt (`N=16384`, `r=8`, `p=1` by default). New encrypted payloads authenticate the `v1.aes-256-gcm` header as AES-GCM AAD; decrypting without AAD remains supported only for payloads produced by older package versions. - -```typescript -type SymmetricKeyDerivationOptions = { - N?: number; - p?: number; - r?: number; - salt: string | StringValueObject | Buffer; -}; - -type CryptoPassword = string | StringValueObject | Password; - -type SymmetricKeyCryptOptions = { - aad?: string | StringValueObject | Buffer; -}; - -class SymmetricKey extends ValueObject { - public static fromBase64(key: string | StringValueObject): SymmetricKey; - public static fromBuffer(key: Buffer): SymmetricKey; - public static generate(): SymmetricKey; - public static fromPassword( - password: CryptoPassword, - options: SymmetricKeyDerivationOptions, - ): Promise; - public static fromPasswordUsingOwasp( - password: CryptoPassword, - options: Pick, - ): Promise; - constructor(value: string | StringValueObject); - public getBuffer(): Buffer; - public encrypt(payload: CryptoPayload, options?: SymmetricKeyCryptOptions): SymmetricEncryptedPayload; - public decrypt(encryptedPayload: EncryptedPayload, options?: SymmetricKeyCryptOptions): Buffer; -} -``` - -**Example:** -```typescript -// Random symmetric key -const key = SymmetricKey.generate(); -const encrypted = key.encrypt('confidential data'); -const decrypted = key.decrypt(encrypted); -console.log(decrypted.toString()); // 'confidential data' - -// OWASP-aligned deterministic key derivation from password + explicit salt -const password = new Password('Secure-password-123!'); -const derived = await SymmetricKey.fromPasswordUsingOwasp(password, { - salt: 'application-specific-salt', -}); - -// The same password, salt, and scrypt parameters derive the same key -const sameDerived = await SymmetricKey.fromPasswordUsingOwasp(password, { - salt: 'application-specific-salt', -}); -console.log(derived.isEqual(sameDerived)); // true -``` - -The derived key is deterministic, but encryption is not: `encrypt()` generates a fresh 12-byte AES-GCM IV for each payload. Callers must store or reproduce the salt used for password-based derivation; it is not embedded in `SymmetricEncryptedPayload`. `fromPassword()` keeps its original scrypt defaults for backward compatibility. `fromPasswordUsingOwasp()` uses this package's OWASP-aligned scrypt profile for new password-derived keys. - -#### EncryptedPrivateKey - -Represents an immutable password-protected private key container. New encrypted private keys use v3 with scrypt (N=16384, r=8, p=5), a strict Base64-encoded 16-byte salt, a 32-byte derived key, and AES-256-GCM with a 12-byte IV and 16-byte authentication tag. The v3 `v3.scrypt.N16384.r8.p5` header is authenticated as AES-GCM AAD. The class also supports v2 (`N=16384`, `r=8`, `p=1`) and the legacy 4-part format, which decrypts with PBKDF2-SHA256 using 100000 iterations and AES-256-GCM. - -The current encrypted format is: `v3.scrypt.N16384.r8.p5.salt.iv.tag.cipherText` (base64-encoded, dot-separated). v2 format: `v2.scrypt.N16384.r8.p1.salt.iv.tag.cipherText`. Legacy format: `cipherText.iv.salt.tag`. - -The v3 implementation derives a `SymmetricKey` from the password, salt, and -scrypt parameters, then reuses the same AES-256-GCM payload primitive used by -`SymmetricKey`. `EncryptedPrivateKey` keeps its own serialized container because -it must persist the KDF name, KDF parameters, and salt needed to decrypt the -private key later. - -```typescript -class EncryptedPrivateKey extends ValueObject { - public static async create( - privateKey: PrivateKey, - password: CryptoPassword, - ): Promise; - constructor(encryptedPrivateKey: string | StringValueObject); - public decrypt(password: CryptoPassword): Promise; - public needsReEncryption(): boolean; -} -``` - -**Example:** -```typescript -// Encrypt a private key -const password = new Password('Secure-password-123!'); -const encrypted = await EncryptedPrivateKey.create(privateKey, password); -console.log(encrypted.valueOf()); // 'v3.scrypt.N16384.r8.p5.base64Salt.base64IV.base64Tag.base64CipherText' - -// Decrypt it back -const decrypted = await encrypted.decrypt(password); -console.log(decrypted.valueOf()); // Original PEM private key - -// Check if re-encryption is needed -if (encrypted.needsReEncryption()) { - // Re-encrypt with stronger parameters - const reEncrypted = await EncryptedPrivateKey.create(decrypted, password); -} - -// Wrong password throws an error -try { - await encrypted.decrypt(new Password('Wrong-password-123!')); // Throws (AES-GCM authentication fails) -} catch (error) { - console.error('Invalid password'); -} -``` +https://haskou.github.io/value-objects/ -#### EncryptedKeyPair - -Manages a public key paired with an encrypted private key. Allows signing, verification, encryption, and decryption without exposing the raw private key β€” the password is required for signing and decryption. - -```typescript -class EncryptedKeyPair { - public static async encryptKeyPair( - publicKey: PublicKey, - privateKey: PrivateKey, - password: CryptoPassword, - ): Promise; - public static fromPrimitives(primitives: PrimitiveOf): EncryptedKeyPair; - constructor(publicKey: PublicKey, encryptedPrivateKey: EncryptedPrivateKey); - public sign(payload: CryptoPayload, password: CryptoPassword): Promise; - public isValidSignature(payload: CryptoPayload, signature: Signature): boolean; - public encrypt(payload: CryptoPayload): EncryptedPayload; - public decrypt(encryptedPayload: EncryptedPayload, password: CryptoPassword): Promise; - public toPrimitives(): { publicKey: string; encryptedPrivateKey: string }; -} -``` - -**Example:** -```typescript -// Create from a KeyPair -const keyPair = await KeyPair.generate(); -const password = new Password('Secure-password-123!'); -const encrypted = await keyPair.encryptKeyPair(password); - -// Encrypt a payload (no password needed β€” uses public key) -const cipherPayload = encrypted.encrypt('confidential data'); - -// Decrypt (requires the password to unlock the private key internally) -const plaintext = await encrypted.decrypt(cipherPayload, password); -console.log(plaintext.toString()); // 'confidential data' - -// Sign (requires the password) -const sig = await encrypted.sign('message', password); - -// Verify (no password needed) -console.log(encrypted.isValidSignature('message', sig)); // true - -// Serialize and restore -const primitives = encrypted.toPrimitives(); -const restored = EncryptedKeyPair.fromPrimitives(primitives); -``` - -#### EncryptedPayload - -Represents an immutable dot-separated encrypted payload container. It is the base type accepted by decryptors and can classify the known payload shape as `asymmetric`, `symmetric`, or `unknown`. - -```typescript -type EncryptedPayloadScheme = 'asymmetric' | 'symmetric' | 'unknown'; - -class EncryptedPayload extends ValueObject { - public getScheme(): EncryptedPayloadScheme; -} - -class AsymmetricEncryptedPayload extends EncryptedPayload { - public getScheme(): EncryptedPayloadScheme; -} - -class SymmetricEncryptedPayload extends EncryptedPayload { - public getScheme(): EncryptedPayloadScheme; -} -``` - -`AsymmetricEncryptedPayload` is created by `PublicKey.encrypt()`, `KeyPair.encrypt()`, or `EncryptedKeyPair.encrypt()` and decrypted by the corresponding private-key decryptor. `SymmetricEncryptedPayload` is created by `SymmetricKey.encrypt()` and decrypted by `SymmetricKey.decrypt()`. - -Recognized payload shapes: -- Asymmetric v2: `v2.x25519-hkdf-sha256-aes-256-gcm.ephemeralPub.iv.cipherText.tag` -- Asymmetric legacy: `ephemeralPub.iv.cipherText.tag` -- Symmetric v1: `v1.aes-256-gcm.iv.cipherText.tag` - -For current asymmetric v2 and symmetric v1 payloads, the version and algorithm -header is authenticated as AES-GCM AAD. The legacy asymmetric shape and -pre-AAD symmetric payloads still decrypt for backward compatibility. - -#### Encrypting and Decrypting Payloads - -The public-key payload API uses a classical hybrid encryption approach: - -1. The Ed25519 public key is converted to X25519 (Montgomery form) -2. A new ephemeral X25519 key pair is generated for each encryption -3. X25519 produces a shared secret -4. HKDF-SHA256 over the shared secret derives a 256-bit AES key, using `ephemeralPublicKey || recipientPublicKey` as salt and `@haskou/value-objects/asymmetric-payload/v2` as domain-separated info -5. AES-256-GCM encrypts the payload with a 12-byte random IV, 16-byte authentication tag, and `v2.x25519-hkdf-sha256-aes-256-gcm` as AAD -6. The output is `v2.x25519-hkdf-sha256-aes-256-gcm.ephemeralPub.iv.cipherText.tag` with Base64-encoded key, IV, ciphertext, and tag fields -7. Decryption requires the corresponding Ed25519 private key material converted to X25519 - -**With KeyPair (plaintext private key available):** -```typescript -const keyPair = await KeyPair.generate(); - -// Encrypt with public key -const encrypted = keyPair.encrypt('sensitive data'); - -// Decrypt with private key -const decrypted = keyPair.decrypt(encrypted); -console.log(decrypted.toString()); // 'sensitive data' -``` - -**With EncryptedKeyPair (private key protected by password):** -```typescript -const password = new Password('Secure-password-123!'); -const encryptedKeyPair = await keyPair.encryptKeyPair(password); - -// Encrypt (no password needed) -const encrypted = encryptedKeyPair.encrypt('confidential'); - -// Decrypt (password unlocks the private key internally) -const decrypted = await encryptedKeyPair.decrypt(encrypted, password); -console.log(decrypted.toString()); // 'confidential' -``` - -**Low-level with individual keys:** -```typescript -const publicKey = new PublicKey(publicPem); -const privateKey = new PrivateKey(privatePem); - -const encrypted = publicKey.encrypt('hello world'); -const decrypted = privateKey.decrypt(encrypted); -console.log(decrypted.toString()); // 'hello world' -``` - -**Important:** -- Each call to `encrypt()` generates a new ephemeral key, producing different ciphertext even for the same payload. -- Each call to `encrypt()` also generates a fresh 12-byte AES-GCM IV; the 16-byte tag authenticates the ciphertext and current header. -- HKDF uses payload-specific salt and info values so the asymmetric payload key derivation is domain separated from other derivation contexts. -- Decrypting with the wrong private key throws an AES-GCM authentication error. -- The current asymmetric encrypted format is `v2.x25519-hkdf-sha256-aes-256-gcm.ephemeralPub.iv.cipherText.tag` (base64 data fields, dot-separated). -- The previous `ephemeralPub.iv.cipherText.tag` format still decrypts for backward compatibility. -- Asymmetric payload encryption is capped at 1 MiB before encryption. -- Asymmetric payload encryption does not authenticate the sender. AES-GCM authenticates - ciphertext integrity for the derived key, but the payload does not include a - sender key or signature. -- This public-key payload encryption format is library-specific, not HPKE, and is not post-quantum. - -#### Symmetric Payload Encryption - -`SymmetricKey` encrypts and decrypts payloads with AES-256-GCM using the key bytes held by the value object. - -1. A `SymmetricKey` contains exactly 32 bytes (256 bits) encoded as Base64 -2. `encrypt()` generates a fresh 12-byte random IV -3. AES-256-GCM encrypts the payload, authenticates `v1.aes-256-gcm` as AAD by default, and produces a 16-byte authentication tag -4. The output is `v1.aes-256-gcm.iv.cipherText.tag` with Base64-encoded IV, ciphertext, and tag fields -5. `decrypt()` validates the version, algorithm, IV length, tag length, Base64 fields, and 8 MiB ciphertext limit before decrypting - -**Random key:** -```typescript -const key = SymmetricKey.generate(); -const encrypted = key.encrypt('secret data'); -const decrypted = key.decrypt(encrypted); -console.log(decrypted.toString()); // 'secret data' -``` - -**Password-derived key:** -```typescript -const password = new Password('Secure-password-123!'); -const key = await SymmetricKey.fromPasswordUsingOwasp(password, { - salt: 'application-specific-salt', -}); - -const encrypted = key.encrypt(Buffer.from('secret data')); -const decrypted = key.decrypt(encrypted); -console.log(decrypted.toString()); // 'secret data' -``` - -**Custom AAD:** -```typescript -const encrypted = key.encrypt('secret data', { aad: 'orders.v1' }); -const decrypted = key.decrypt(encrypted, { aad: 'orders.v1' }); -``` - -**Important:** -- A 32-byte key is the AES-256 key size. With a uniformly random key, brute-force key search is not practical with current classical computing. -- Password-derived keys are only as strong as the password and salt policy. Use a unique, non-empty salt per derivation context; callers must store or reproduce that salt because it is not included in `SymmetricEncryptedPayload`. -- `SymmetricKey.fromPassword()` keeps the original scrypt defaults (`N=16384`, `r=8`, `p=1`) for backward compatibility. Custom scrypt parameters are accepted through the same method. -- `SymmetricKey.fromPasswordUsingOwasp()` uses the package's OWASP-aligned scrypt profile (`N=16384`, `r=8`, `p=5`) for new password-derived symmetric keys. -- The same password, salt, and scrypt parameters derive the same key. The encrypted payload remains randomized because every encryption uses a fresh 96-bit IV. -- New symmetric payloads authenticate their version and algorithm header as AAD. If a caller supplies custom AAD, the same AAD is required for decryption. -- `decrypt()` falls back to no-AAD decryption only when no custom AAD is supplied, so symmetric payloads created before header AAD support remain readable. -- AES-GCM requires IV uniqueness for a given key. The library generates a random IV for each encryption; avoid manually reusing serialized payload internals as new encryption inputs. -- Symmetric payload encryption provides confidentiality and ciphertext integrity for holders of the same key. It does not identify which holder encrypted the payload. It is not a post-quantum cryptography scheme; AES-256 is considered to retain a large security margin against Grover-style quadratic speedups, but the KDF and password entropy still matter. -- Symmetric payload encryption is capped at 8 MiB before encryption. - -#### CryptoPayload - -Type alias for values that can be signed, verified, or encrypted: - -```typescript -type CryptoPayload = string | StringValueObject | Buffer | Media; -``` - -### Media Value Objects - -#### Media - -Represents immutable binary or string content with Buffer, size, and Base64 conversion utilities. - -```typescript -class Media extends ValueObject { - constructor(value: string | Buffer); - public getBuffer(): Buffer; - public getSize(): number; - public getBase64(): string; -} -``` - -**Example:** -```typescript -// From a string -const media = new Media('hello world'); -console.log(media.valueOf()); // 'hello world' -console.log(media.getSize()); // 11 -console.log(media.getBase64()); // 'aGVsbG8gd29ybGQ=' -console.log(media.getBuffer()); // - -// From a Buffer -const fromBuffer = new Media(Buffer.from('binary data')); -console.log(fromBuffer.getSize()); // 11 - -// Null safety -const nullMedia = new Media(undefined as unknown as string); -console.log(NullObject.isNullObject(nullMedia)); // true -``` - -### Collection Utilities - -#### UniqueObjectArray - -Provides an iterable collection that keeps only unique items based on each item's `isEqual()` implementation. - -```typescript -interface ComparableItem { - isEqual(item: unknown): boolean; -} - -class UniqueObjectArray implements Iterable { - public static fromArray(array: T[]): UniqueObjectArray; - public includes(item: T): boolean; - public push(item: T): boolean; - public remove(item: T): boolean; - public length(): number; - public toArray(): T[]; - public [Symbol.iterator](): Iterator; -} -``` - -**Example:** -```typescript -const weekdays = UniqueObjectArray.fromArray([ - DayOfWeek.MONDAY, - DayOfWeek.TUESDAY, - DayOfWeek.MONDAY, -]); - -console.log(weekdays.length()); // 2 -console.log(weekdays.includes(DayOfWeek.MONDAY)); // true - -weekdays.push(DayOfWeek.THURSDAY); -weekdays.remove(DayOfWeek.TUESDAY); - -console.log([...weekdays].map((day) => day.toString())); // ['monday', 'thursday'] -``` - -### Hour Value Objects - -#### Hour - -Represents immutable time values in 24-hour format with time arithmetic operations. - -```typescript -class Hour extends StringValueObject { - constructor(value: string); // "HH:MM" format - constructor(value: number, minutes?: number); // Separate hours and minutes - - // Time arithmetic - public addMinutes(minutes: number): Hour; - public diffInMinutes(other: Hour): number; - - // Getters - public getHours(): number; - public getMinutes(): number; - - // Comparisons - public isGreaterThan(hour: Hour): boolean; - public isLessThan(hour: Hour): boolean; -} -``` - -**Example:** -```typescript -// Different constructor options -const morning = new Hour('09:30'); // String format -const afternoon = new Hour(14, 45); // Separate values -const evening = new Hour(20, 0); // 20:00 - -// Time arithmetic -const laterTime = morning.addMinutes(90); // 11:00 -const duration = morning.diffInMinutes(afternoon); // 315 minutes - -// Getters -console.log(morning.getHours()); // 9 -console.log(morning.getMinutes()); // 30 - -// Comparisons -console.log(afternoon.isGreaterThan(morning)); // true -console.log(morning.isLessThan(evening)); // true - -// String representation -console.log(morning.toString()); // '09:30' - -// Handles day overflow -const lateNight = new Hour('23:30'); -const nextDay = lateNight.addMinutes(60); // '00:30' (next day) - -// Validation -try { - new Hour('25:00'); // Throws InvalidHourError - new Hour(12, 65); // Throws InvalidMinutesError -} catch (error) { - console.error('Invalid time format'); -} -``` - -### Time Value Objects - -#### CalendarDay - -Represents immutable calendar day values with date utilities. - -```typescript -class CalendarDay { - constructor(value?: string | Date | number | Timestamp); - - public static fromTimestamp(timestamp: Timestamp): CalendarDay; - public static fromString(value: string): CalendarDay; - - public toString(): string; - public getDayOfWeek(): DayOfWeek; - public isBefore(other: CalendarDay): boolean; - public isAfter(other: CalendarDay): boolean; - public diffInDays(other: CalendarDay): number; -} -``` - -#### Day - -Represents immutable day values (1-31) with validation. - -```typescript -class Day extends Integer { - constructor(value: number | NumberValueObject); -} -``` - -#### DayOfWeek - -Represents immutable day of week enumeration. - -```typescript -enum EDaysOfWeek { - MONDAY = 'monday', - TUESDAY = 'tuesday', - WEDNESDAY = 'wednesday', - THURSDAY = 'thursday', - FRIDAY = 'friday', - SATURDAY = 'saturday', - SUNDAY = 'sunday', -} - -class DayOfWeek extends Enum { - protected getValues() { - return Object.values(EDaysOfWeek); - } -} -``` - -#### Duration - -Represents immutable duration values in milliseconds. - -```typescript -class Duration extends NumberValueObject { - constructor(value: number | NumberValueObject); - - public static fromHours(hours: number): Duration; - public static fromMinutes(minutes: number): Duration; - public static fromSeconds(seconds: number): Duration; - - public toHours(): number; - public toMinutes(): number; - public toSeconds(): number; -} -``` - -#### Month - -Represents immutable month enumeration (1-12). - -```typescript -class Month extends Enum { - protected getValues() { - return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - } -} -``` - -#### MonthOfYear - -Represents immutable month/year combination. - -```typescript -class MonthOfYear extends ValueObject { - constructor(month: number | Month, year: number | Year); - - public static fromTimestamp(timestamp: Timestamp): MonthOfYear; - public static fromString(value: string): MonthOfYear; - - public getMonth(): Month; - public getYear(): Year; - public toString(): string; // Format: "YYYY/MM" -} -``` - -#### Timestamp - -Represents immutable timestamp values with comprehensive date/time operations. - -```typescript -class Timestamp extends ValueObject { - constructor(value: number | Date | Timestamp | string); - - // Static factory methods - public static now(): Timestamp; - public static fromDate(date: Date): Timestamp; - - // Getters - public getYear(): Year; - public getMonth(): Month; - public getDay(): Day; - public getHours(): number; - public getMinutes(): number; - public getSeconds(): number; - public getDayOfWeek(): DayOfWeek; - - // Arithmetic operations - public addDays(days: number): Timestamp; - public addHours(hours: number): Timestamp; - public addMinutes(minutes: number): Timestamp; - public addSeconds(seconds: number): Timestamp; - - // Comparisons - public isBefore(other: Timestamp): boolean; - public isAfter(other: Timestamp): boolean; - public diffInDays(other: Timestamp): number; - public diffInHours(other: Timestamp): number; - public diffInMinutes(other: Timestamp): number; - - // Conversions - public toDate(): Date; - public toISOString(): string; -} -``` - -#### TimestampInterval - -Represents immutable time intervals with start and end timestamps. - -```typescript -class TimestampInterval extends ValueObject { - constructor(start: Timestamp, end: Timestamp); - - public static fromPrimitives(primitives: PrimitiveOf): TimestampInterval; - - public getStart(): Timestamp; - public getEnd(): Timestamp; - public getDuration(): Duration; - public contains(timestamp: Timestamp): boolean; - public overlaps(other: TimestampInterval): boolean; - public split(parts: number): TimestampInterval[]; -} -``` - -### Enum Value Objects - -#### Enum - -An abstract base class for creating type-safe enumeration value objects. - -```typescript -abstract class Enum extends ValueObject { - constructor(protected readonly value: T); - - public abstract getValues(): T[]; -} -``` - -**Features:** -- Type-safe enumeration validation -- Inheritance from ValueObject -- Static factory methods support -- Immutable enum values -- Comprehensive error handling - -**Usage:** - -```typescript -import { Enum } from 'value-objects'; - -// Define your enum -enum UserStatus { - ACTIVE = 'active', - INACTIVE = 'inactive', - PENDING = 'pending', - SUSPENDED = 'suspended' -} - -enum Priority { - LOW = 1, - MEDIUM = 2, - HIGH = 3, - URGENT = 4 -} - -// Create enum value object classes -class UserStatusEnum extends Enum { - public getValues() { - return Object.values(UserStatus); - } - - // Static factory methods for convenience - static ACTIVE = () => new UserStatusEnum(UserStatus.ACTIVE); - static INACTIVE = () => new UserStatusEnum(UserStatus.INACTIVE); - static PENDING = () => new UserStatusEnum(UserStatus.PENDING); - static SUSPENDED = () => new UserStatusEnum(UserStatus.SUSPENDED); -} - -class PriorityEnum extends Enum { - public getValues() { - return Object.values(Priority); - } - - static LOW = () => new PriorityEnum(Priority.LOW); - static MEDIUM = () => new PriorityEnum(Priority.MEDIUM); - static HIGH = () => new PriorityEnum(Priority.HIGH); - static URGENT = () => new PriorityEnum(Priority.URGENT); -} - -// Usage examples -const status = UserStatusEnum.ACTIVE(); -const priority = new PriorityEnum(Priority.HIGH); - -console.log(status.toString()); // 'active' -console.log(priority.valueOf()); // 3 -console.log(status.isEqual('active')); // true -console.log(priority.isEqual(3)); // true - -// Comparison -const status1 = UserStatusEnum.ACTIVE(); -const status2 = new UserStatusEnum('active'); -console.log(status1.isEqual(status2)); // true - -// Clone -const statusCopy = (status as any).clone(); -console.log(statusCopy.toString()); // 'active' - -// Validation -try { - new UserStatusEnum('invalid'); // Throws ValueNotInEnumError - new PriorityEnum(999); // Throws ValueNotInEnumError -} catch (error) { - console.error('Invalid enum value'); -} - -// Mixed type enums -enum MixedEnum { - STRING_VALUE = 'text', - NUMBER_VALUE = 42 -} - -class MixedEnumValueObject extends Enum { - public getValues() { - return Object.values(MixedEnum); - } -} - -const mixedString = new MixedEnumValueObject('text'); // Valid -const mixedNumber = new MixedEnumValueObject(42); // Valid -``` - -## 🚨 Error Handling - -The library provides specific error types for different validation failures: - -```typescript -import { - InvalidStringLengthError, - InvalidNumberError, - InvalidIntegerError, - InvalidColorError, - InvalidHourError, - InvalidMinutesError, - InvalidEmailError, - InvalidPositiveNumberError, - ValueNotInEnumError, - InvalidDayError, - InvalidDayFormatError, - InvalidTimestampIntervalError, - NullObjectError -} from 'value-objects'; - -// String length validation -try { - new StringValueObject('too long string', 5); -} catch (error) { - if (error instanceof InvalidStringLengthError) { - console.error('String exceeds maximum length'); - } -} - -// Number validation -try { - new NumberValueObject(NaN); -} catch (error) { - if (error instanceof InvalidNumberError) { - console.error('Invalid number provided'); - } -} - -// Integer validation -try { - new Integer(42.5); -} catch (error) { - if (error instanceof InvalidIntegerError) { - console.error('Value must be a whole number'); - } -} - -try { - new Integer(Infinity); -} catch (error) { - if (error instanceof InvalidIntegerError) { - console.error('Infinity is not a valid integer'); - } -} - -// Color validation -try { - new Color('not-a-color'); -} catch (error) { - if (error instanceof InvalidColorError) { - console.error('Invalid hex color format'); - } -} - -// Time validation -try { - new Hour('25:30'); // Invalid hour -} catch (error) { - if (error instanceof InvalidHourError) { - console.error('Hour must be between 0-23'); - } -} - -try { - new Hour(12, 75); // Invalid minutes -} catch (error) { - if (error instanceof InvalidMinutesError) { - console.error('Minutes must be between 0-59'); - } -} - -// Day validation -try { - new Day(32); // Invalid day -} catch (error) { - if (error instanceof InvalidDayError) { - console.error('Day must be between 1-31'); - } -} - -// Calendar day format validation -try { - new CalendarDay('invalid-date-format'); -} catch (error) { - if (error instanceof InvalidDayFormatError) { - console.error('Invalid date format provided'); - } -} - -// Email validation -try { - new Email('invalid-email-format'); -} catch (error) { - if (error instanceof InvalidEmailError) { - console.error('Invalid email format provided'); - } -} - -try { - new Email('user@domain.c'); // Domain extension too short -} catch (error) { - if (error instanceof InvalidEmailError) { - console.error('Email domain extension must be 2-13 characters'); - } -} - -// PositiveNumber validation -try { - new PositiveNumber(-5); -} catch (error) { - if (error instanceof InvalidPositiveNumberError) { - console.error('Value must be greater than 0'); - } -} - -try { - new PositiveNumber(0); // Zero is not positive -} catch (error) { - if (error instanceof InvalidPositiveNumberError) { - console.error('Zero is not a positive number'); - } -} - -// Enum validation -try { - new UserStatusEnum('invalid-status'); -} catch (error) { - if (error instanceof ValueNotInEnumError) { - console.error('Value not found in enum: active,inactive,pending,suspended'); - } -} - -try { - new PriorityEnum(999); // Invalid number enum -} catch (error) { - if (error instanceof ValueNotInEnumError) { - console.error('Enum value must be one of: 1,2,3,4'); - } -} - -// Timestamp interval validation -try { - const start = new Timestamp(Date.now() + 1000); - const end = new Timestamp(Date.now()); - new TimestampInterval(start, end); // Start after end -} catch (error) { - if (error instanceof InvalidTimestampIntervalError) { - console.error('Start timestamp must be before end timestamp'); - } -} - -// Null object error -try { - const nullValue = new StringValueObject(null); - nullValue.isEmpty(); // Throws NullObjectError -} catch (error) { - if (error instanceof NullObjectError) { - console.error('Cannot call method on null object'); - } -} -``` - -## 🎯 Design Principles - -### Immutability - -All value objects are immutable. Operations that appear to modify values actually return new instances: - -```typescript -const original = new NumberValueObject(10); -const modified = original.add(5); - -console.log(original.valueOf()); // 10 (unchanged) -console.log(modified.valueOf()); // 15 (new instance) -``` - -### Value Equality - -Value objects are compared by their values, not by reference: - -```typescript -const a = new StringValueObject('hello'); -const b = new StringValueObject('hello'); - -console.log(a === b); // false (different instances) -console.log(a.isEqual(b)); // true (same value) -``` - -### Null Safety - -The library properly handles null and undefined values using the Null Object pattern: - -```typescript -const nullValue = new StringValueObject(undefined); -console.log(NullObject.isNullObject(nullValue)); // true -``` - -If you try to call any method from a NullObject it will throw -a NullObjectError: - -```typescript -// NullObject StringValue instance -const nullValue = new StringValueObject(undefined); -nullValue.isEmpty(); // Throw NullObjectError! -``` - -This prevents defensive programming and if-else validations - -```typescript -// Without NullObject Pattern -let valueObject: StringValueObject; -if (value) { - valueObject = new StringValueObject(value); -} -else { - throw Error('Invalid value'); -} -return valueObject.isEmpty(); - -// With NullObject pattern -valueObject = new StringValueObject(value); -return valueObject.isEmpty(); -``` - -### Type Safety - -The library provides full TypeScript support with strict typing to prevent runtime errors: - -```typescript -// Compile-time type checking -const amount: NumberValueObject = new NumberValueObject(100); -const price: PositiveNumber = new PositiveNumber(29.99); - -// Type inference works correctly -const total = amount.add(price); // Type: NumberValueObject -const isExpensive = price.isGreaterThan(50); // Type: boolean -``` - -### Validation by Construction - -All validation happens during object construction, ensuring that invalid states are impossible: - -```typescript -// These will throw errors immediately -new Email('invalid-email'); // InvalidEmailError -new PositiveNumber(-1); // InvalidPositiveNumberError -new Integer(3.14); // InvalidIntegerError -new Color('not-a-color'); // InvalidColorError -``` - -### Composition over Inheritance - -Value objects can be composed together to create more complex domain models: - -```typescript -class Product { - constructor( - private readonly name: StringValueObject, - private readonly price: PositiveNumber, - private readonly category: CategoryEnum - ) {} - - public getName(): StringValueObject { - return this.name; - } - - public getPrice(): PositiveNumber { - return this.price; - } - - public getCategory(): CategoryEnum { - return this.category; - } -} - -const product = new Product( - new StringValueObject('Laptop'), - new PositiveNumber(999.99), - CategoryEnum.ELECTRONICS() -); -``` +This file exists only as a pointer to avoid maintaining a second documentation source. diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 0000000..29b7074 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,197 @@ +import { defineConfig } from 'vitepress'; + +const referenceSidebar = [ + { + text: 'Overview', + items: [ + { text: 'Reference overview', link: '/reference/' }, + ], + }, + { + text: 'Base', + collapsed: false, + items: [ + { text: 'ValueObject', link: '/reference/value-object' }, + { text: 'NullObject', link: '/reference/null-object' }, + { text: 'Enum', link: '/reference/enum' }, + ], + }, + { + text: 'Strings', + collapsed: false, + items: [ + { text: 'StringValueObject', link: '/reference/string-value-object' }, + { text: 'Email', link: '/reference/email' }, + { text: 'Color', link: '/reference/color' }, + { text: 'Password', link: '/reference/password' }, + ], + }, + { + text: 'Numbers', + collapsed: false, + items: [ + { text: 'NumberValueObject', link: '/reference/number-value-object' }, + { text: 'Integer', link: '/reference/integer' }, + { text: 'PositiveNumber', link: '/reference/positive-number' }, + ], + }, + { + text: 'Identifiers', + collapsed: true, + items: [ + { text: 'UUID', link: '/reference/uuid' }, + { text: 'ShortId', link: '/reference/short-id' }, + ], + }, + { + text: 'Time', + collapsed: true, + items: [ + { text: 'Timestamp', link: '/reference/timestamp' }, + { text: 'TimestampInterval', link: '/reference/timestamp-interval' }, + { text: 'CalendarDay', link: '/reference/calendar-day' }, + { text: 'Duration', link: '/reference/duration' }, + { text: 'Hour', link: '/reference/hour' }, + { text: 'Day', link: '/reference/day' }, + { text: 'DayOfWeek', link: '/reference/day-of-week' }, + { text: 'Month', link: '/reference/month' }, + { text: 'MonthOfYear', link: '/reference/month-of-year' }, + { text: 'Year', link: '/reference/year' }, + ], + }, + { + text: 'Coordinates', + collapsed: true, + items: [ + { text: 'Latitude', link: '/reference/latitude' }, + { text: 'Longitude', link: '/reference/longitude' }, + { text: 'Coordinates', link: '/reference/coordinates' }, + ], + }, + { + text: 'Hashes', + collapsed: true, + items: [ + { text: 'Hash', link: '/reference/hash' }, + { text: 'MD5Hash', link: '/reference/md5-hash' }, + { text: 'SHA256Hash', link: '/reference/sha256-hash' }, + { text: 'SHA512Hash', link: '/reference/sha512-hash' }, + ], + }, + { + text: 'Media and collections', + collapsed: true, + items: [ + { text: 'Media', link: '/reference/media' }, + { text: 'UniqueObjectArray', link: '/reference/unique-object-array' }, + ], + }, + { + text: 'Crypto helpers', + collapsed: true, + items: [ + { text: 'Crypto notes', link: '/reference/crypto-notes' }, + { text: 'Key', link: '/reference/key' }, + { text: 'PrivateKey', link: '/reference/private-key' }, + { text: 'PublicKey', link: '/reference/public-key' }, + { text: 'KeyPair', link: '/reference/key-pair' }, + { text: 'SymmetricKey', link: '/reference/symmetric-key' }, + { text: 'Signature', link: '/reference/signature' }, + { text: 'EncryptedPrivateKey', link: '/reference/encrypted-private-key' }, + { text: 'EncryptedPayload', link: '/reference/encrypted-payload' }, + { text: 'AsymmetricEncryptedPayload', link: '/reference/asymmetric-encrypted-payload' }, + { text: 'SymmetricEncryptedPayload', link: '/reference/symmetric-encrypted-payload' }, + { text: 'EncryptedKeyPair', link: '/reference/encrypted-key-pair' }, + ], + }, +]; + +export default defineConfig({ + lang: 'en-US', + title: 'Value Objects', + description: 'Documentation for @haskou/value-objects.', + base: '/value-objects/', + lastUpdated: true, + cleanUrls: true, + + head: [ + ['meta', { name: 'theme-color', content: '#3f6ee8' }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { property: 'og:title', content: 'Value Objects' }], + ['meta', { property: 'og:description', content: 'Documentation for @haskou/value-objects.' }], + ], + + themeConfig: { + logo: { src: '/logo.svg', alt: 'Value Objects' }, + siteTitle: 'Value Objects', + + nav: [ + { text: 'Guide', link: '/getting-started/introduction' }, + { text: 'Reference', link: '/reference/' }, + { text: 'Agent skill', link: '/guides/agent-skill' }, + { text: 'npm', link: 'https://www.npmjs.com/package/@haskou/value-objects' }, + ], + + sidebar: { + '/getting-started/': [ + { + text: 'Getting started', + items: [ + { text: 'Introduction', link: '/getting-started/introduction' }, + { text: 'Installation', link: '/getting-started/installation' }, + { text: 'Basic usage', link: '/getting-started/basic-usage' }, + ], + }, + { + text: 'Guides', + items: [ + { text: 'Custom value objects', link: '/guides/custom-value-objects' }, + { text: 'Error handling', link: '/guides/error-handling' }, + { text: 'Serialization', link: '/guides/serialization' }, + { text: 'Null object', link: '/guides/null-object' }, + { text: 'Release flow', link: '/guides/release-flow' }, + { text: 'Agent skill', link: '/guides/agent-skill' }, + ], + }, + ], + '/guides/': [ + { + text: 'Guides', + items: [ + { text: 'Custom value objects', link: '/guides/custom-value-objects' }, + { text: 'Error handling', link: '/guides/error-handling' }, + { text: 'Serialization', link: '/guides/serialization' }, + { text: 'Null object', link: '/guides/null-object' }, + { text: 'Release flow', link: '/guides/release-flow' }, + { text: 'Agent skill', link: '/guides/agent-skill' }, + ], + }, + ...referenceSidebar, + ], + '/reference/': referenceSidebar, + }, + + outline: { + level: [2, 3], + label: 'On this page', + }, + + search: { + provider: 'local', + }, + + socialLinks: [ + { icon: 'github', link: 'https://github.com/haskou/value-objects' }, + ], + + editLink: { + pattern: 'https://github.com/haskou/value-objects/edit/master/docs/:path', + text: 'Edit this page on GitHub', + }, + + footer: { + message: 'Released under the MIT License.', + copyright: 'Copyright Β© Haskou', + }, + }, +}); diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css new file mode 100644 index 0000000..4e88fff --- /dev/null +++ b/docs/.vitepress/theme/custom.css @@ -0,0 +1,27 @@ +:root { + --vp-c-brand-1: #315edb; + --vp-c-brand-2: #3f6ee8; + --vp-c-brand-3: #6f91f2; + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: linear-gradient(120deg, #315edb, #7c3aed); +} + +.VPDoc h1 code, +.VPDoc h2 code, +.VPDoc h3 code { + font-size: 0.92em; +} + +.vp-doc table code { + white-space: nowrap; +} + +.vp-doc .custom-block.info code, +.vp-doc .custom-block.tip code, +.vp-doc .custom-block.warning code { + color: inherit; +} + +.vp-doc a[href^='https://github.com/haskou/ddd-engineer-skills'] { + font-weight: 600; +} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 0000000..149273e --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,4 @@ +import DefaultTheme from 'vitepress/theme'; +import './custom.css'; + +export default DefaultTheme; diff --git a/docs/404.md b/docs/404.md new file mode 100644 index 0000000..a8c8764 --- /dev/null +++ b/docs/404.md @@ -0,0 +1,10 @@ +--- +title: Not found +description: Page not found +--- + +# Page not found + +The page does not exist. + +Return to the [documentation home](/). diff --git a/docs/getting-started/basic-usage.md b/docs/getting-started/basic-usage.md new file mode 100644 index 0000000..e61f11c --- /dev/null +++ b/docs/getting-started/basic-usage.md @@ -0,0 +1,59 @@ +--- +title: Basic usage +description: Basic usage examples for @haskou/value-objects +--- + +# Basic usage + +## Create values + +```typescript +import { Color, Email, Hour, PositiveNumber } from '@haskou/value-objects'; + +const email = new Email('user@example.com'); +const color = new Color('#FF0000'); +const startAt = new Hour('09:30'); +const quantity = new PositiveNumber(3); +``` + +## Read primitive values + +```typescript +email.valueOf(); // 'user@example.com' +email.toString(); // 'user@example.com' +quantity.valueOf(); // 3 +``` + +## Compare by value + +```typescript +const a = new Email('user@example.com'); +const b = new Email('user@example.com'); + +a === b; // false +a.isEqual(b); // true +``` + +## Use helpers + +```typescript +const nextSlot = new Hour('23:30').addMinutes(90); +nextSlot.toString(); // '01:00' + +const id = UUID.generate(); +id.toString(); +``` + +## Handle invalid input + +```typescript +import { Email, InvalidEmailError } from '@haskou/value-objects'; + +try { + new Email('nope'); +} catch (error) { + if (error instanceof InvalidEmailError) { + // Handle invalid email input. + } +} +``` diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..e310e8e --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,34 @@ +--- +title: Installation +description: Install @haskou/value-objects +--- + +# Installation + +```bash +npm install @haskou/value-objects +``` + +```bash +yarn add @haskou/value-objects +``` + +```bash +pnpm add @haskou/value-objects +``` + +## Requirements + +The package declares Node.js `>=20.19.0`. + +## Importing + +```typescript +import { Email, Timestamp, UUID } from '@haskou/value-objects'; +``` + +The package exposes the compiled CommonJS entrypoint and TypeScript declarations through the package root. + +## Published files + +Only `dist` is published. Source files are not part of the npm package payload. diff --git a/docs/getting-started/introduction.md b/docs/getting-started/introduction.md new file mode 100644 index 0000000..ebdb7c1 --- /dev/null +++ b/docs/getting-started/introduction.md @@ -0,0 +1,46 @@ +--- +title: Introduction +description: Introduction to @haskou/value-objects +--- + +# Introduction + +`@haskou/value-objects` provides small immutable objects around primitive values. + +A value object validates input at construction time, exposes a primitive through `valueOf()`, supports value equality through `isEqual()`, and usually provides small helpers specific to that value. + +```typescript +import { Email, PositiveNumber } from '@haskou/value-objects'; + +const email = new Email('user@example.com'); +const amount = new PositiveNumber(10); + +email.valueOf(); // 'user@example.com' +amount.isGreaterThan(5); // true +``` + +## What the package contains + +| Area | Classes | +| --- | --- | +| Base | `ValueObject`, `NullObject`, `Enum` | +| Text | `StringValueObject`, `Password`, `Email`, `Color` | +| Numbers | `NumberValueObject`, `Integer`, `PositiveNumber` | +| IDs | `ShortId`, `UUID` | +| Time | `Timestamp`, `CalendarDay`, `Day`, `DayOfWeek`, `Duration`, `Hour`, `Month`, `MonthOfYear`, `TimestampInterval`, `Year` | +| Location | `Latitude`, `Longitude`, `Coordinates` | +| Hashes | `Hash`, `MD5Hash`, `SHA256Hash`, `SHA512Hash` | +| Media | `Media` | +| Collections | `UniqueObjectArray` | +| Crypto | `Key`, `PrivateKey`, `PublicKey`, `Signature`, `KeyPair`, encrypted payload and key helpers | + +## Design expectations + +The package keeps the public API small: + +- Construct an object. +- Let it validate itself. +- Use `valueOf()`, `toString()`, or specific methods. +- Catch specific errors when invalid input matters. + +The library is not tied to one architecture. Use it anywhere validated values help: request parsing, config loading, persistence mapping, tests, scripts, CLI tools, or application code. diff --git a/docs/guides/agent-skill.md b/docs/guides/agent-skill.md new file mode 100644 index 0000000..7af01b3 --- /dev/null +++ b/docs/guides/agent-skill.md @@ -0,0 +1,29 @@ +--- +title: Agent skill +description: Link to the reusable agent skill repository. +--- + +# Agent skill + +A reusable agent skill is available here: + +https://github.com/haskou/ddd-engineer-skills + +The skill repository is separate from this package. This keeps the runtime library focused on code, tests, and documentation, while the agent skill can evolve independently. + +## Use cases + +The skill can help coding agents with repetitive maintenance tasks around this style of value object: + +- creating a new value object with validation and tests +- reviewing public exports +- checking serialization behavior +- checking error behavior +- keeping examples consistent with the package API + +## Repository split + +| Repository | Purpose | +| --- | --- | +| `haskou/value-objects` | Runtime package and documentation. | +| `haskou/ddd-engineer-skills` | Reusable agent instructions and workflows. | diff --git a/docs/guides/custom-value-objects.md b/docs/guides/custom-value-objects.md new file mode 100644 index 0000000..f593347 --- /dev/null +++ b/docs/guides/custom-value-objects.md @@ -0,0 +1,63 @@ +--- +title: Custom value objects +description: Create custom value objects with @haskou/value-objects +--- + +# Custom value objects + +You can extend the base classes when the package does not include the exact value you need. + +## Custom string value + +```typescript +import { + InvalidStringLengthError, + StringValueObject, +} from '@haskou/value-objects'; + +class DisplayName extends StringValueObject { + constructor(value: string | StringValueObject) { + super(value, 80); + } +} +``` + +## Custom number value + +```typescript +import { NumberValueObject } from '@haskou/value-objects'; + +class Percentage extends NumberValueObject { + constructor(value: number | NumberValueObject) { + super(value); + + if (this.isLessThan(0) || this.isGreaterThan(100)) { + throw new Error('Percentage must be between 0 and 100'); + } + } +} +``` + +## Custom enum value + +```typescript +import { Enum } from '@haskou/value-objects'; + +enum Visibility { + PUBLIC = 'public', + PRIVATE = 'private', +} + +class VisibilityValue extends Enum { + public getValues(): string[] { + return Object.values(Visibility); + } +} +``` + +## Tips + +- Validate in the constructor. +- Prefer specific errors if callers need to branch on the failure. +- Keep methods small and tied to the wrapped value. +- Return new instances for operations that conceptually change the value. diff --git a/docs/guides/error-handling.md b/docs/guides/error-handling.md new file mode 100644 index 0000000..65b6c7b --- /dev/null +++ b/docs/guides/error-handling.md @@ -0,0 +1,60 @@ +--- +title: Error handling +description: Error handling guide for @haskou/value-objects +--- + +# Error handling + +Most validation failures throw specific errors. Catch the specific error when code needs to distinguish between invalid value categories. + +```typescript +import { Email, InvalidEmailError } from '@haskou/value-objects'; + +try { + new Email('invalid'); +} catch (error) { + if (error instanceof InvalidEmailError) { + // Invalid email format. + } +} +``` + +## Common errors + +| Error | Usually thrown by | +| --- | --- | +| `InvalidStringLengthError` | `StringValueObject` | +| `InvalidPasswordError` | `Password` | +| `InvalidNumberError` | `NumberValueObject` | +| `InvalidIntegerError` | `Integer`, `Year`, `Day` | +| `InvalidPositiveNumberError` | `PositiveNumber` | +| `InvalidEmailError` | `Email` | +| `InvalidColorError` | `Color` | +| `InvalidLengthError` | IDs, keys, payloads | +| `InvalidFormatError` | IDs, keys, payloads | +| `InvalidHashError` | Hash classes | +| `InvalidDayError` | `Day`, `CalendarDay`, `DayOfWeek` | +| `InvalidDayFormatError` | `CalendarDay` | +| `InvalidHourError` | `Hour` | +| `InvalidMinutesError` | `Hour` | +| `InvalidTimestampIntervalError` | `TimestampInterval` | +| `ValueNotInEnumError` | `Enum` subclasses | +| `NullObjectError` | Calling a fake method on a null object | + +## Pattern + +```typescript +function parseEmail(value: string): Email | null { + try { + return new Email(value); + } catch (error) { + if (error instanceof InvalidEmailError) { + return null; + } + + throw error; + } +} +``` + +Keep catch blocks narrow. Broadly swallowing validation errors is how data quality goes to die quietly in production. diff --git a/docs/guides/null-object.md b/docs/guides/null-object.md new file mode 100644 index 0000000..9d0e8dd --- /dev/null +++ b/docs/guides/null-object.md @@ -0,0 +1,37 @@ +--- +title: Null objects +description: Null object behavior in @haskou/value-objects +--- + +# Null objects + +`ValueObject` returns a null object when constructed with `null` or `undefined`. + +```typescript +import { NullObject, StringValueObject } from '@haskou/value-objects'; + +const value = new StringValueObject(undefined as never); + +NullObject.isNullObject(value); // true +value.valueOf(); // undefined +``` + +## Method calls + +Null objects expose fake methods from the target class. Calling those methods throws `NullObjectError`. + +```typescript +const value = new StringValueObject(undefined as never); + +value.isEmpty(); // throws NullObjectError +``` + +## When to check + +```typescript +if (NullObject.isNullObject(value)) { + // Handle missing value explicitly. +} +``` + +Use this when input can be absent and the calling code wants to preserve the value-object shape until a method is actually used. diff --git a/docs/guides/release-flow.md b/docs/guides/release-flow.md new file mode 100644 index 0000000..0552886 --- /dev/null +++ b/docs/guides/release-flow.md @@ -0,0 +1,35 @@ +--- +title: Release flow +description: Release branch and publishing flow +--- + +# Release flow + +Publishing is handled by CI when a pull request is merged into the default branch. + +| Branch prefix | npm version bump | +| --- | --- | +| `fix/*` | Patch | +| `feat/*` | Minor | +| `break/*` | Major | + +Branches without one of these prefixes still run CI, but they do not publish to npm. + +## Before opening a release PR + +```bash +yarn lint +yarn test +yarn build +``` + +## What CI publishes + +The package publishes only the compiled `dist` directory. + +The package metadata declares: + +- CommonJS entrypoint: `dist/index.js` +- Type declarations: `dist/index.d.ts` +- `sideEffects: false` +- Public npm access diff --git a/docs/guides/serialization.md b/docs/guides/serialization.md new file mode 100644 index 0000000..47c5083 --- /dev/null +++ b/docs/guides/serialization.md @@ -0,0 +1,46 @@ +--- +title: Serialization +description: Serialization notes for @haskou/value-objects +--- + +# Serialization + +Most value objects serialize through `valueOf()`. + +```typescript +const email = new Email('user@example.com'); + +JSON.stringify({ email: email.valueOf() }); +``` + +## Objects with primitive helpers + +Some objects provide explicit primitive helpers. + +```typescript +const interval = new TimestampInterval(start, end); +const primitives = interval.toPrimitives(); +const restored = TimestampInterval.fromPrimitives(primitives); +``` + +`KeyPair` and `EncryptedKeyPair` also provide `toPrimitives()` / `fromPrimitives()` helpers. + +## Rebuilding values + +Deserialize by passing primitive values back into constructors or factory methods. + +```typescript +const stored = { + email: 'user@example.com', + createdAt: 1782218400000, +}; + +const email = new Email(stored.email); +const createdAt = new Timestamp(stored.createdAt); +``` + +## Notes + +- `valueOf()` is the safest default for persistence payloads. +- Use explicit helper methods when available. +- For encrypted data, persist all required fields, including salts when deriving keys from passwords. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ed6fb54 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,50 @@ +--- +title: Documentation +description: Documentation for @haskou/value-objects. +--- + +# Value Objects documentation + +A TypeScript library for validated, immutable primitive wrappers and small utility value objects. + +::: info Documentation +This site is the main documentation for `@haskou/value-objects`. +Use it as the source of truth for installation, examples, errors, serialization notes, and the API reference. +::: + +## Start here + +- [Introduction](/getting-started/introduction) +- [Installation](/getting-started/installation) +- [Basic usage](/getting-started/basic-usage) +- [API reference](/reference/) + +## Package + +```bash +npm install @haskou/value-objects +``` + +```bash +yarn add @haskou/value-objects +``` + +## Reference sections + +| Section | Contents | +| --- | --- | +| [Base objects](/reference/value-object) | `ValueObject`, `NullObject`, `Enum` | +| [Strings and numbers](/reference/string-value-object) | `StringValueObject`, `Email`, `Color`, `Password`, `NumberValueObject`, `Integer`, `PositiveNumber` | +| [Identifiers](/reference/uuid) | `UUID`, `ShortId` | +| [Time](/reference/timestamp) | `Timestamp`, `TimestampInterval`, `CalendarDay`, `Duration`, `Hour`, `Day`, `Year`, `Month`, `MonthOfYear`, `DayOfWeek` | +| [Coordinates](/reference/coordinates) | `Latitude`, `Longitude`, `Coordinates` | +| [Hashes](/reference/hash) | `Hash`, `MD5Hash`, `SHA256Hash`, `SHA512Hash` | +| [Media](/reference/media) | `Media` | +| [Collections](/reference/unique-object-array) | `UniqueObjectArray` | +| [Crypto helpers](/reference/crypto-notes) | `KeyPair`, `PrivateKey`, `PublicKey`, `SymmetricKey`, payload value objects | + +## Agent skill + +Reusable agent instructions and engineering skills live in [`haskou/ddd-engineer-skills`](https://github.com/haskou/ddd-engineer-skills). + +This repository documents and ships the runtime library. The skill repository is separate on purpose. diff --git a/docs/public/logo.svg b/docs/public/logo.svg new file mode 100644 index 0000000..0e138b8 --- /dev/null +++ b/docs/public/logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/reference/asymmetric-encrypted-payload.md b/docs/reference/asymmetric-encrypted-payload.md new file mode 100644 index 0000000..7e6a571 --- /dev/null +++ b/docs/reference/asymmetric-encrypted-payload.md @@ -0,0 +1,56 @@ +--- +title: AsymmetricEncryptedPayload +description: Encrypted payload addressed to a public key. +--- + +# `AsymmetricEncryptedPayload` + +Encrypted payload addressed to a public key. + +## Import + +```typescript +import { AsymmetricEncryptedPayload } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class AsymmetricEncryptedPayload extends EncryptedPayload +``` + +## Constructor + +```typescript +constructor(value: string) +``` + +## Validation + +Created by public-key encryption helpers. Recognized as asymmetric by `getScheme()`. + +## Methods + +| Method | Description | +| --- | --- | +| `getScheme()` | Returns `'asymmetric'`. | + +## Example + +```typescript +import { KeyPair } from '@haskou/value-objects'; + +const keyPair = await KeyPair.generate(); +const encrypted = keyPair.encrypt('secret'); + +encrypted.getScheme(); // 'asymmetric' +``` + +## Notes + +- Current format starts with `v2.x25519-hkdf-sha256-aes-256-gcm`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/calendar-day.md b/docs/reference/calendar-day.md new file mode 100644 index 0000000..e8f91b5 --- /dev/null +++ b/docs/reference/calendar-day.md @@ -0,0 +1,72 @@ +--- +title: CalendarDay +description: UTC calendar day value object formatted as YYYY-MM-DD. +--- + +# `CalendarDay` + +UTC calendar day value object formatted as YYYY-MM-DD. + +## Import + +```typescript +import { CalendarDay } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class CalendarDay extends ValueObject +``` + +## Constructor + +```typescript +constructor(value?: string | Date | number | Timestamp) +``` + +## Validation + +String input must match `YYYY-M-D` or `YYYY-MM-DD` style and resolve to a valid date. + +## Throws + +This class can throw: + +- `InvalidDayError` +- `InvalidDayFormatError` + +## Methods + +| Method | Description | +| --- | --- | +| `getYear()` | Returns the numeric year. | +| `getMonth()` | Returns a `Month`. | +| `getMonthOfYear()` | Returns a `MonthOfYear`. | +| `getDay()` | Returns a `Day`. | +| `getDayOfWeek()` | Returns a `DayOfWeek`. | +| `toTimestamp()` | Returns a UTC timestamp for the day. | +| `isBefore(date)` | Compares by `YYYY-MM-DD` string value. | +| `isAfter(date)` | Compares by `YYYY-MM-DD` string value. | + +## Example + +```typescript +import { CalendarDay } from '@haskou/value-objects'; + +const day = new CalendarDay('2026-06-23'); + +day.toString(); // '2026-06-23' +day.getMonth().valueOf(); // 6 +day.getDayOfWeek().toString(); // depends on date +``` + +## Notes + +- When no value is provided, it uses the current timestamp. +- Internally it uses UTC date getters through `Timestamp`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/color.md b/docs/reference/color.md new file mode 100644 index 0000000..cc9d69e --- /dev/null +++ b/docs/reference/color.md @@ -0,0 +1,62 @@ +--- +title: Color +description: Validated hex color value object. +--- + +# `Color` + +Validated hex color value object. + +## Import + +```typescript +import { Color } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Color extends StringValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be a 3-digit or 6-digit hex color with a leading `#`. + +## Throws + +This class can throw: + +- `InvalidColorError` + +## Methods + +| Method | Description | +| --- | --- | +| `isEqual(other)` | Compares color values case-insensitively. | + +## Example + +```typescript +import { Color } from '@haskou/value-objects'; + +const red = new Color('#ff0000'); + +red.isEqual(Color.RED); // true +Color.BLUE.valueOf(); // '#0000FF' +``` + +## Notes + +- Predefined constants include red, green, blue, black, white, yellow, cyan, magenta, orange, purple, pink, and brown. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/coordinates.md b/docs/reference/coordinates.md new file mode 100644 index 0000000..aad6640 --- /dev/null +++ b/docs/reference/coordinates.md @@ -0,0 +1,58 @@ +--- +title: Coordinates +description: Latitude and longitude pair represented as a string value. +--- + +# `Coordinates` + +Latitude and longitude pair represented as a string value. + +## Import + +```typescript +import { Coordinates } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Coordinates extends StringValueObject +``` + +## Constructor + +```typescript +constructor(latitude: number | Latitude, longitude: number | Longitude) +``` + +## Validation + +Latitude and longitude are validated through their own classes. + +## Methods + +| Method | Description | +| --- | --- | +| `static fromString(value)` | Parses `latitude,longitude` into a `Coordinates` instance. | +| `getLatitude()` | Returns a `Latitude` copy. | +| `getLongitude()` | Returns a `Longitude` copy. | + +## Example + +```typescript +import { Coordinates } from '@haskou/value-objects'; + +const coordinates = new Coordinates(41.3888, 2.159); + +coordinates.toString(); // '41.3888,2.159' +coordinates.getLatitude().valueOf(); // 41.3888 +``` + +## Notes + +- `fromString()` uses `parseFloat()` on both comma-separated parts. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/crypto-notes.md b/docs/reference/crypto-notes.md new file mode 100644 index 0000000..bb8780e --- /dev/null +++ b/docs/reference/crypto-notes.md @@ -0,0 +1,85 @@ +--- +title: Crypto notes +description: Technical notes for cryptographic value objects. +--- + +# Crypto notes + +This page documents the cryptographic value objects. It is intentionally outside the README so the README stays readable by mammals and hiring managers, a demographic overlap still under investigation. + +## Key types + +- `PrivateKey` stores an Ed25519 private key in PEM PKCS8 format. +- `PublicKey` stores an Ed25519 public key in PEM SPKI format. +- `Signature` stores an Ed25519 signature as Base64. +- `KeyPair` groups a public and private key. +- `EncryptedKeyPair` groups a public key with an encrypted private key. + +## Signing + +```typescript +const keyPair = await KeyPair.generate(); +const signature = keyPair.sign('message'); + +keyPair.isValidSignature('message', signature); // true +``` + +## Asymmetric payload encryption + +Asymmetric payload encryption uses a library-specific hybrid scheme. + +Current format: + +```text +v2.x25519-hkdf-sha256-aes-256-gcm.ephemeralPublicKey.iv.cipherText.tag +``` + +The high-level flow is: + +1. Convert Ed25519 key material to X25519. +2. Generate ephemeral X25519 key material for each encryption. +3. Derive a shared secret. +4. Derive an AES key with HKDF-SHA256. +5. Encrypt the payload with AES-256-GCM. +6. Authenticate the current version and algorithm header as AAD. + +Legacy asymmetric payloads with four parts are still recognized by `PrivateKey.decrypt()` for backward compatibility. + +## Symmetric payload encryption + +`SymmetricKey` stores a 32-byte AES-256-GCM key encoded as Base64. + +Current format: + +```text +v1.aes-256-gcm.iv.cipherText.tag +``` + +`SymmetricKey.encrypt()` generates a fresh 12-byte IV per encryption. + +## Password-derived symmetric keys + +```typescript +const key = await SymmetricKey.fromPasswordUsingOwasp(password, { + salt: 'application-specific-salt', +}); +``` + +The derived key is deterministic for the same password, salt, and scrypt parameters. Encryption remains non-deterministic because a fresh IV is generated for each payload. + +The salt is not embedded in `SymmetricEncryptedPayload`. Callers must store or reproduce it. + +## Encrypted private keys + +New encrypted private keys use the current v3 handler. + +The public container shape is versioned. Older formats are still accepted when recognized by the internal version handlers, and `needsReEncryption()` indicates whether the stored key should be upgraded. + +## Limits and caveats + +- Asymmetric payload encryption is capped at 1 MiB before encryption. +- Symmetric payload encryption is capped at 8 MiB before encryption. +- The asymmetric payload format is library-specific, not HPKE. +- The implementation is classical cryptography, not post-quantum cryptography. +- Payload encryption is recipient-addressed encryption with ciphertext integrity. It is not a forward-secret transport protocol. +- These helpers should not be presented as an independently audited protocol. diff --git a/docs/reference/day-of-week.md b/docs/reference/day-of-week.md new file mode 100644 index 0000000..742662f --- /dev/null +++ b/docs/reference/day-of-week.md @@ -0,0 +1,65 @@ +--- +title: DayOfWeek +description: Enum value object for weekdays. +--- + +# `DayOfWeek` + +Enum value object for weekdays. + +## Import + +```typescript +import { DayOfWeek } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class DayOfWeek extends Enum +``` + +## Constructor + +```typescript +constructor(value: EDaysOfWeek | string) +``` + +## Validation + +Must be one of `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday`. + +## Throws + +This class can throw: + +- `ValueNotInEnumError` +- `InvalidDayError` + +## Methods + +| Method | Description | +| --- | --- | +| `static fromNumber(day)` | Maps `Date#getUTCDay()` numbers to a `DayOfWeek`. | +| `static fromTimestamp(timestamp)` | Builds from a `Timestamp`. | +| `getValues()` | Returns all valid weekday strings. | +| `toNumber()` | Returns the JavaScript UTC weekday number. | + +## Example + +```typescript +import { DayOfWeek } from '@haskou/value-objects'; + +DayOfWeek.MONDAY.toString(); // 'monday' +DayOfWeek.SUNDAY.toNumber(); // 0 +DayOfWeek.fromNumber(2).toString(); // 'tuesday' +``` + +## Notes + +- Static constants are provided for every weekday. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/day.md b/docs/reference/day.md new file mode 100644 index 0000000..f8df8f7 --- /dev/null +++ b/docs/reference/day.md @@ -0,0 +1,54 @@ +--- +title: Day +description: Day-of-month value object. +--- + +# `Day` + +Day-of-month value object. + +## Import + +```typescript +import { Day } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Day extends Integer +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +Must be between `1` and `31`, inclusive. + +## Throws + +This class can throw: + +- `InvalidDayError` + +## Example + +```typescript +import { Day } from '@haskou/value-objects'; + +const day = new Day(23); +day.valueOf(); // 23 +``` + +## Notes + +- It does not know which month it belongs to. `31` is valid even for months that do not have 31 days. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/duration.md b/docs/reference/duration.md new file mode 100644 index 0000000..254dd11 --- /dev/null +++ b/docs/reference/duration.md @@ -0,0 +1,70 @@ +--- +title: Duration +description: Duration value object stored in milliseconds. +--- + +# `Duration` + +Duration value object stored in milliseconds. + +## Import + +```typescript +import { Duration } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Duration extends NumberValueObject +``` + +## Constructor + +```typescript +constructor(milliseconds: NumberValueObject | Duration) +``` + +## Validation + +Uses `NumberValueObject` validation. + +## Methods + +| Method | Description | +| --- | --- | +| `static fromDays(days)` | Creates a duration from days. | +| `static fromHours(hours)` | Creates a duration from hours. | +| `static fromMinutes(minutes)` | Creates a duration from minutes. | +| `static fromSeconds(seconds)` | Creates a duration from seconds. | +| `static fromMilliseconds(milliseconds)` | Creates a duration from milliseconds. | +| `getTotalDays()` | Returns total days as `NumberValueObject`. | +| `getTotalHours()` | Returns total hours as `NumberValueObject`. | +| `getTotalMinutes()` | Returns total minutes as `NumberValueObject`. | +| `getTotalSeconds()` | Returns total seconds as `NumberValueObject`. | +| `getTotalMilliseconds()` | Returns total milliseconds as `NumberValueObject`. | +| `getDays()` | Returns the day component. | +| `getHours()` | Returns the hour component. | +| `getMinutes()` | Returns the minute component. | +| `getSeconds()` | Returns the second component. | +| `getMilliseconds()` | Returns the millisecond component. | + +## Example + +```typescript +import { Duration } from '@haskou/value-objects'; + +const duration = Duration.fromMinutes(90); + +duration.valueOf(); // 5400000 +duration.getTotalHours().valueOf(); // 1.5 +``` + +## Notes + +- Month and year factors are fixed approximations: 30 days and 365 days. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/email.md b/docs/reference/email.md new file mode 100644 index 0000000..d7a0980 --- /dev/null +++ b/docs/reference/email.md @@ -0,0 +1,57 @@ +--- +title: Email +description: Validated email address value object. +--- + +# `Email` + +Validated email address value object. + +## Import + +```typescript +import { Email } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Email extends StringValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must match `/^[\w+\-.]+@(?:[\w-]+\.)+[a-zA-Z]{2,13}$/`. + +## Throws + +This class can throw: + +- `InvalidEmailError` + +## Example + +```typescript +import { Email } from '@haskou/value-objects'; + +const email = new Email('user+tag@example.co.uk'); + +email.valueOf(); // 'user+tag@example.co.uk' +email.isEqual('user+tag@example.co.uk'); // true +``` + +## Notes + +- Validation is intentionally format-based. +- The class does not perform DNS checks, mailbox checks, or provider-specific normalization. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/encrypted-key-pair.md b/docs/reference/encrypted-key-pair.md new file mode 100644 index 0000000..061797b --- /dev/null +++ b/docs/reference/encrypted-key-pair.md @@ -0,0 +1,66 @@ +--- +title: EncryptedKeyPair +description: Public key plus encrypted private key helper. +--- + +# `EncryptedKeyPair` + +Public key plus encrypted private key helper. + +## Import + +```typescript +import { EncryptedKeyPair } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class EncryptedKeyPair +``` + +## Constructor + +```typescript +constructor(publicKey: PublicKey, encryptedPrivateKey: EncryptedPrivateKey) +``` + +## Validation + +The constructor relies on `PublicKey` and `EncryptedPrivateKey` validation. + +## Methods + +| Method | Description | +| --- | --- | +| `static encryptKeyPair(publicKey, privateKey, password)` | Encrypts the private key and returns an encrypted pair. | +| `static fromPrimitives(primitives)` | Restores from `{ publicKey, encryptedPrivateKey }`. | +| `isValidSignature(payload, signature)` | Verifies using the public key. | +| `sign(payload, password)` | Decrypts the private key internally and signs. | +| `toPrimitives()` | Returns `{ encryptedPrivateKey, publicKey }`. | +| `encrypt(payload)` | Encrypts using the public key. | +| `decrypt(encryptedPayload, password)` | Decrypts after unlocking the private key. | + +## Example + +```typescript +import { KeyPair, Password } from '@haskou/value-objects'; + +const keyPair = await KeyPair.generate(); +const password = new Password('Secure-password-123!'); +const encryptedPair = await keyPair.encryptKeyPair(password); + +const payload = encryptedPair.encrypt('secret'); +const plaintext = await encryptedPair.decrypt(payload, password); + +plaintext.toString(); // 'secret' +``` + +## Notes + +- Signing and decrypting require the password. Verification and encryption do not. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/encrypted-payload.md b/docs/reference/encrypted-payload.md new file mode 100644 index 0000000..676705a --- /dev/null +++ b/docs/reference/encrypted-payload.md @@ -0,0 +1,54 @@ +--- +title: EncryptedPayload +description: Base dot-separated encrypted payload container. +--- + +# `EncryptedPayload` + +Base dot-separated encrypted payload container. + +## Import + +```typescript +import { EncryptedPayload } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class EncryptedPayload extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string) +``` + +## Validation + +Stores the payload string. Scheme detection is done by shape. + +## Methods + +| Method | Description | +| --- | --- | +| `getScheme()` | Returns `'asymmetric'`, `'symmetric'`, or `'unknown'`. | + +## Example + +```typescript +import { EncryptedPayload } from '@haskou/value-objects'; + +const payload = new EncryptedPayload('v1.aes-256-gcm.iv.cipherText.tag'); +payload.getScheme(); // 'symmetric' +``` + +## Notes + +- Used as the base type accepted by decryptors. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/encrypted-private-key.md b/docs/reference/encrypted-private-key.md new file mode 100644 index 0000000..1737968 --- /dev/null +++ b/docs/reference/encrypted-private-key.md @@ -0,0 +1,67 @@ +--- +title: EncryptedPrivateKey +description: Password-protected private key container. +--- + +# `EncryptedPrivateKey` + +Password-protected private key container. + +## Import + +```typescript +import { EncryptedPrivateKey } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class EncryptedPrivateKey extends ValueObject +``` + +## Constructor + +```typescript +constructor(encryptedPrivateKey: string | StringValueObject) +``` + +## Validation + +Accepted formats are delegated to version handlers. Unknown formats throw during `decrypt()`. + +## Throws + +This class can throw: + +- `InvalidEncryptedPrivateKeyFormatError` + +## Methods + +| Method | Description | +| --- | --- | +| `static create(privateKey, password)` | Encrypts a private key using the current v3 format. | +| `decrypt(password)` | Decrypts and returns a `PrivateKey`. | +| `needsReEncryption()` | Returns true when the stored format should be upgraded. | + +## Example + +```typescript +import { EncryptedPrivateKey, Password, PrivateKey } from '@haskou/value-objects'; + +const privateKey = PrivateKey.generate(); +const password = new Password('Secure-password-123!'); +const encrypted = await EncryptedPrivateKey.create(privateKey, password); + +const restored = await encrypted.decrypt(password); +restored.isEqual(privateKey); // true +``` + +## Notes + +- New encrypted private keys use the v3 handler. +- Legacy and v2 formats are still readable when recognized by the internal handlers. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/enum.md b/docs/reference/enum.md new file mode 100644 index 0000000..4290415 --- /dev/null +++ b/docs/reference/enum.md @@ -0,0 +1,70 @@ +--- +title: Enum +description: Abstract base class for enum-backed value objects. +--- + +# `Enum` + +Abstract base class for enum-backed value objects. + +## Import + +```typescript +import { Enum } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +abstract class Enum extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: T) +``` + +## Validation + +The value must be included in the array returned by `getValues()`. + +## Throws + +This class can throw: + +- `ValueNotInEnumError` + +## Methods + +| Method | Description | +| --- | --- | +| `getValues()` | Abstract method. Return all allowed primitive enum values. | + +## Example + +```typescript +import { Enum } from '@haskou/value-objects'; + +enum Status { + ACTIVE = 'active', + DISABLED = 'disabled', +} + +class StatusValue extends Enum { + public getValues(): string[] { + return Object.values(Status); + } +} + +new StatusValue(Status.ACTIVE).valueOf(); // 'active' +``` + +## Notes + +- Works with string, number, and mixed primitive enum values. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/hash.md b/docs/reference/hash.md new file mode 100644 index 0000000..0e5e8d6 --- /dev/null +++ b/docs/reference/hash.md @@ -0,0 +1,54 @@ +--- +title: Hash +description: Abstract base class for hash value objects. +--- + +# `Hash` + +Abstract base class for hash value objects. + +## Import + +```typescript +import { Hash } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +abstract class Hash extends ValueObject +``` + +## Constructor + +```typescript +constructor(source: string | StringValueObject) +``` + +## Validation + +Concrete subclasses validate length and algorithm-specific format. + +## Methods + +| Method | Description | +| --- | --- | +| `toBase64()` | Converts the hex hash into a Base64 `StringValueObject`. | + +## Example + +```typescript +import { MD5Hash } from '@haskou/value-objects'; + +const hash = MD5Hash.from('hello'); +hash.toBase64().valueOf(); // 'XUFAKrxLKna5cZ2REBfFkg==' +``` + +## Notes + +- Use concrete subclasses directly. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/hour.md b/docs/reference/hour.md new file mode 100644 index 0000000..21261b6 --- /dev/null +++ b/docs/reference/hour.md @@ -0,0 +1,70 @@ +--- +title: Hour +description: 24-hour HH:MM value object. +--- + +# `Hour` + +24-hour HH:MM value object. + +## Import + +```typescript +import { Hour } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Hour extends StringValueObject +``` + +## Constructor + +```typescript +constructor(value: string) | constructor(value: number, minutes?: number) +``` + +## Validation + +Hours must be 0 to 23. Minutes must be 0 to 59. String input must contain exactly two colon-separated numeric parts. + +## Throws + +This class can throw: + +- `InvalidHourError` +- `InvalidMinutesError` + +## Methods + +| Method | Description | +| --- | --- | +| `addMinutes(minutes)` | Returns a new `Hour`, wrapping around the day. | +| `diffInMinutes(other)` | Returns forward distance in minutes, wrapping to the next day when needed. | +| `getHours()` | Returns numeric hours. | +| `getMinutes()` | Returns numeric minutes. | +| `isGreaterThan(hour)` | Compares by time-of-day. | +| `isLessThan(hour)` | Compares by time-of-day. | + +## Example + +```typescript +import { Hour } from '@haskou/value-objects'; + +const start = new Hour('09:30'); +const end = start.addMinutes(90); + +end.toString(); // '11:00' +start.diffInMinutes(new Hour('10:00')); // 30 +new Hour('23:30').addMinutes(60).toString(); // '00:30' +``` + +## Notes + +- String output is always zero-padded as `HH:MM`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..1061e26 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,66 @@ +--- +title: Reference +description: API reference for @haskou/value-objects +--- + +# Reference + +Each exported class has its own page. Tiny miracle: now changing one class doc no longer means spelunking through a mega Markdown crypt. + +## Base + +- [`ValueObject`](/reference/value-object) +- [`NullObject`](/reference/null-object) +- [`Enum`](/reference/enum) +- [`UniqueObjectArray`](/reference/unique-object-array) + +## Primitive wrappers + +- [`StringValueObject`](/reference/string-value-object) +- [`Password`](/reference/password) +- [`NumberValueObject`](/reference/number-value-object) +- [`Integer`](/reference/integer) +- [`PositiveNumber`](/reference/positive-number) +- [`Email`](/reference/email) +- [`Color`](/reference/color) + +## IDs, hashes, media, coordinates + +- [`ShortId`](/reference/short-id) +- [`UUID`](/reference/uuid) +- [`Hash`](/reference/hash) +- [`MD5Hash`](/reference/md5-hash) +- [`SHA256Hash`](/reference/sha256-hash) +- [`SHA512Hash`](/reference/sha512-hash) +- [`Media`](/reference/media) +- [`Latitude`](/reference/latitude) +- [`Longitude`](/reference/longitude) +- [`Coordinates`](/reference/coordinates) + +## Time + +- [`CalendarDay`](/reference/calendar-day) +- [`Day`](/reference/day) +- [`DayOfWeek`](/reference/day-of-week) +- [`Duration`](/reference/duration) +- [`Hour`](/reference/hour) +- [`Month`](/reference/month) +- [`MonthOfYear`](/reference/month-of-year) +- [`Timestamp`](/reference/timestamp) +- [`TimestampInterval`](/reference/timestamp-interval) +- [`Year`](/reference/year) + +## Crypto + +- [`Key`](/reference/key) +- [`PrivateKey`](/reference/private-key) +- [`PublicKey`](/reference/public-key) +- [`Signature`](/reference/signature) +- [`KeyPair`](/reference/key-pair) +- [`EncryptedPayload`](/reference/encrypted-payload) +- [`AsymmetricEncryptedPayload`](/reference/asymmetric-encrypted-payload) +- [`SymmetricEncryptedPayload`](/reference/symmetric-encrypted-payload) +- [`SymmetricKey`](/reference/symmetric-key) +- [`EncryptedPrivateKey`](/reference/encrypted-private-key) +- [`EncryptedKeyPair`](/reference/encrypted-key-pair) +- [`Crypto notes`](/reference/crypto-notes) diff --git a/docs/reference/integer.md b/docs/reference/integer.md new file mode 100644 index 0000000..1774123 --- /dev/null +++ b/docs/reference/integer.md @@ -0,0 +1,57 @@ +--- +title: Integer +description: Whole-number value object. +--- + +# `Integer` + +Whole-number value object. + +## Import + +```typescript +import { Integer } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Integer extends NumberValueObject +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +The value must be a number with no fractional part. + +## Throws + +This class can throw: + +- `InvalidIntegerError` +- `InvalidNumberError` + +## Example + +```typescript +import { Integer } from '@haskou/value-objects'; + +const count = new Integer(42); +count.valueOf(); // 42 +count.add(8).valueOf(); // 50 +``` + +## Notes + +- Arithmetic methods are inherited from `NumberValueObject`. +- Arithmetic results are returned through the base number behavior, not revalidated as `Integer` after every operation. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/key-pair.md b/docs/reference/key-pair.md new file mode 100644 index 0000000..1dcef11 --- /dev/null +++ b/docs/reference/key-pair.md @@ -0,0 +1,67 @@ +--- +title: KeyPair +description: Plain public/private key pair helper. +--- + +# `KeyPair` + +Plain public/private key pair helper. + +## Import + +```typescript +import { KeyPair } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class KeyPair +``` + +## Constructor + +```typescript +constructor(publicKey: PublicKey, privateKey: PrivateKey) +``` + +## Validation + +The constructor relies on `PublicKey` and `PrivateKey` validation. + +## Methods + +| Method | Description | +| --- | --- | +| `static generate()` | Generates a new key pair. | +| `static fromPrimitives(primitives)` | Restores from `{ publicKey, privateKey }`. | +| `encryptKeyPair(password)` | Returns an `EncryptedKeyPair`. | +| `isValidSignature(payload, signature)` | Verifies with the public key. | +| `sign(payload)` | Signs with the private key. | +| `toPrimitives()` | Returns `{ privateKey, publicKey }`. | +| `encrypt(payload)` | Encrypts with the public key. | +| `decrypt(encryptedPayload)` | Decrypts with the private key. | + +## Example + +```typescript +import { KeyPair, Password } from '@haskou/value-objects'; + +const keyPair = await KeyPair.generate(); +const signature = keyPair.sign('message'); + +keyPair.isValidSignature('message', signature); // true + +const encrypted = await keyPair.encryptKeyPair( + new Password('Secure-password-123!'), +); +``` + +## Notes + +- `toPrimitives()` includes the raw private key. Store it carefully. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/key.md b/docs/reference/key.md new file mode 100644 index 0000000..b5f75d6 --- /dev/null +++ b/docs/reference/key.md @@ -0,0 +1,48 @@ +--- +title: Key +description: Abstract base class for PEM key value objects. +--- + +# `Key` + +Abstract base class for PEM key value objects. + +## Import + +```typescript +import { Key } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +abstract class Key extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Concrete key classes validate PEM format and length. + +## Example + +```typescript +import { PrivateKey } from '@haskou/value-objects'; + +const privateKey = PrivateKey.generate(); +privateKey.valueOf(); // PEM string +``` + +## Notes + +- Use `PrivateKey` or `PublicKey` directly. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/latitude.md b/docs/reference/latitude.md new file mode 100644 index 0000000..219c88d --- /dev/null +++ b/docs/reference/latitude.md @@ -0,0 +1,54 @@ +--- +title: Latitude +description: Latitude value object. +--- + +# `Latitude` + +Latitude value object. + +## Import + +```typescript +import { Latitude } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Latitude extends NumberValueObject +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +Must be between `-90` and `90`, inclusive. + +## Throws + +This class can throw: + +- `InvalidLatitudeError` + +## Example + +```typescript +import { Latitude } from '@haskou/value-objects'; + +const latitude = new Latitude(41.3888); +latitude.valueOf(); // 41.3888 +``` + +## Notes + +- Accepts primitive numbers and `NumberValueObject` instances. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/longitude.md b/docs/reference/longitude.md new file mode 100644 index 0000000..7c90923 --- /dev/null +++ b/docs/reference/longitude.md @@ -0,0 +1,54 @@ +--- +title: Longitude +description: Longitude value object. +--- + +# `Longitude` + +Longitude value object. + +## Import + +```typescript +import { Longitude } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Longitude extends NumberValueObject +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +Must be between `-180` and `180`, inclusive. + +## Throws + +This class can throw: + +- `InvalidLongitudeError` + +## Example + +```typescript +import { Longitude } from '@haskou/value-objects'; + +const longitude = new Longitude(2.159); +longitude.valueOf(); // 2.159 +``` + +## Notes + +- Accepts primitive numbers and `NumberValueObject` instances. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/md5-hash.md b/docs/reference/md5-hash.md new file mode 100644 index 0000000..cf6031f --- /dev/null +++ b/docs/reference/md5-hash.md @@ -0,0 +1,62 @@ +--- +title: MD5Hash +description: MD5 hash value object. +--- + +# `MD5Hash` + +MD5 hash value object. + +## Import + +```typescript +import { MD5Hash } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class MD5Hash extends Hash +``` + +## Constructor + +```typescript +constructor(source: string | StringValueObject) +``` + +## Validation + +Must be a 32-character lowercase hex MD5 hash. + +## Throws + +This class can throw: + +- `InvalidHashError` + +## Methods + +| Method | Description | +| --- | --- | +| `static isValid(hash)` | Returns true when the value matches the MD5 hash pattern. | +| `static from(buffer)` | Hashes a string, `StringValueObject`, `Media`, or `Buffer`. | +| `toBase64()` | Inherited from `Hash`. | + +## Example + +```typescript +import { MD5Hash } from '@haskou/value-objects'; + +const hash = MD5Hash.from('hello'); +hash.toString(); // '5d41402abc4b2a76b9719d911017c592' +``` + +## Notes + +- MD5 is available for compatibility and identifiers. Do not use MD5 as a secure password hash. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/media.md b/docs/reference/media.md new file mode 100644 index 0000000..d7c7308 --- /dev/null +++ b/docs/reference/media.md @@ -0,0 +1,60 @@ +--- +title: Media +description: String or Buffer-backed media value object. +--- + +# `Media` + +String or Buffer-backed media value object. + +## Import + +```typescript +import { Media } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Media extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | Buffer) +``` + +## Validation + +Accepts strings and Node `Buffer` values. + +## Methods + +| Method | Description | +| --- | --- | +| `getBuffer()` | Returns a defensive `Buffer` copy. | +| `getSize()` | Returns byte length. | +| `getBase64()` | Returns the content encoded as Base64. | +| `isEqual(other)` | Compares `Media` and `Buffer` values by bytes; falls back to value equality otherwise. | + +## Example + +```typescript +import { Media } from '@haskou/value-objects'; + +const media = new Media('hello world'); + +media.getSize(); // 11 +media.getBase64(); // 'aGVsbG8gd29ybGQ=' +media.getBuffer(); // Buffer +``` + +## Notes + +- When constructed from a Buffer, the Buffer is copied. Returned buffers are also copies. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/month-of-year.md b/docs/reference/month-of-year.md new file mode 100644 index 0000000..3b1c5f3 --- /dev/null +++ b/docs/reference/month-of-year.md @@ -0,0 +1,61 @@ +--- +title: MonthOfYear +description: Month and year pair formatted as YYYY/MM. +--- + +# `MonthOfYear` + +Month and year pair formatted as YYYY/MM. + +## Import + +```typescript +import { MonthOfYear } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class MonthOfYear extends ValueObject +``` + +## Constructor + +```typescript +constructor(month: number | Month, year: number | Year) +``` + +## Validation + +Month and year are validated through `Month` and `Year`. + +## Methods + +| Method | Description | +| --- | --- | +| `static fromTimestamp(timestamp)` | Creates a month/year from a timestamp. | +| `static fromString(value)` | Parses `YYYY/MM`. | +| `getMonth()` | Returns a `Month`. | +| `getYear()` | Returns a `Year`. | +| `getNumberOfDays()` | Returns the number of days in that month. | +| `getTimestampInterval()` | Returns a `TimestampInterval` from first day to last day. | + +## Example + +```typescript +import { MonthOfYear } from '@haskou/value-objects'; + +const month = new MonthOfYear(2, 2024); + +month.toString(); // '2024/02' +month.getNumberOfDays(); // 29 +``` + +## Notes + +- The output always pads the month to two digits. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/month.md b/docs/reference/month.md new file mode 100644 index 0000000..65bbd23 --- /dev/null +++ b/docs/reference/month.md @@ -0,0 +1,62 @@ +--- +title: Month +description: Month enum value object. +--- + +# `Month` + +Month enum value object. + +## Import + +```typescript +import { Month } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Month extends Enum +``` + +## Constructor + +```typescript +constructor(value: number) +``` + +## Validation + +Must be an integer value from `1` to `12`. + +## Throws + +This class can throw: + +- `ValueNotInEnumError` + +## Methods + +| Method | Description | +| --- | --- | +| `getValues()` | Returns `[1, 2, ..., 12]`. | +| `getIndex()` | Returns zero-based month index for `Date` APIs. | + +## Example + +```typescript +import { Month } from '@haskou/value-objects'; + +Month.JANUARY.valueOf(); // 1 +Month.JANUARY.getIndex(); // 0 +new Month(12).valueOf(); // 12 +``` + +## Notes + +- Static constants are provided for every month. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/null-object.md b/docs/reference/null-object.md new file mode 100644 index 0000000..5d78df0 --- /dev/null +++ b/docs/reference/null-object.md @@ -0,0 +1,58 @@ +--- +title: NullObject +description: Factory and detector for null object instances. +--- + +# `NullObject` + +Factory and detector for null object instances. + +## Import + +```typescript +import { NullObject } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +abstract class NullObject +``` + +## Constructor + +```typescript +Not constructed directly. +``` + +## Validation + +`NullObject.new()` creates an object with `isNullObject: true`, `valueOf(): undefined`, and fake methods that throw `NullObjectError`. + +## Methods + +| Method | Description | +| --- | --- | +| `static new(klass)` | Creates a null object compatible with the requested class. | +| `static isNullObject(value)` | Returns true when the value looks like a null object. | + +## Example + +```typescript +import { NullObject, StringValueObject } from '@haskou/value-objects'; + +const value = new StringValueObject(undefined as never); + +NullObject.isNullObject(value); // true +value.valueOf(); // undefined +``` + +## Notes + +- Calling most class methods on a null object throws `NullObjectError`. +- This is useful when nullish construction should be represented consistently. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/number-value-object.md b/docs/reference/number-value-object.md new file mode 100644 index 0000000..613cd88 --- /dev/null +++ b/docs/reference/number-value-object.md @@ -0,0 +1,73 @@ +--- +title: NumberValueObject +description: Immutable number wrapper with comparison and arithmetic helpers. +--- + +# `NumberValueObject` + +Immutable number wrapper with comparison and arithmetic helpers. + +## Import + +```typescript +import { NumberValueObject } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class NumberValueObject extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +The value must not be `NaN`. + +## Throws + +This class can throw: + +- `InvalidNumberError` + +## Methods + +| Method | Description | +| --- | --- | +| `isZero()` | Returns true when the number is exactly 0. | +| `isGreaterThan(other)` | Returns true when the current value is greater than `other`. | +| `isGreaterOrEqualThan(other)` | Returns true when the current value is greater than or equal to `other`. | +| `isLessThan(other)` | Returns true when the current value is less than `other`. | +| `isLessOrEqualThan(other)` | Returns true when the current value is less than or equal to `other`. | +| `add(other)` | Returns a new `NumberValueObject` with the sum. | +| `subtract(other)` | Returns a new `NumberValueObject` with the difference. | +| `multiply(other)` | Returns a new `NumberValueObject` with the product. | +| `divide(other)` | Returns a new `NumberValueObject` with the quotient. | + +## Example + +```typescript +import { NumberValueObject } from '@haskou/value-objects'; + +const a = new NumberValueObject(10); +const b = new NumberValueObject(5); + +a.add(b).valueOf(); // 15 +a.subtract(3).valueOf(); // 7 +a.isGreaterThan(b); // true +``` + +## Notes + +- Arithmetic helpers return new instances. +- `Infinity` is accepted because the current validation only rejects `NaN`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/password.md b/docs/reference/password.md new file mode 100644 index 0000000..c5f2c1b --- /dev/null +++ b/docs/reference/password.md @@ -0,0 +1,55 @@ +--- +title: Password +description: Validated password string value object. +--- + +# `Password` + +Validated password string value object. + +## Import + +```typescript +import { Password } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Password extends StringValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be 12 to 256 characters long and include at least one uppercase letter, one lowercase letter, one number, and one non-alphanumeric symbol. + +## Throws + +This class can throw: + +- `InvalidPasswordError` + +## Example + +```typescript +import { Password } from '@haskou/value-objects'; + +const password = new Password('Secure-password-123!'); +password.valueOf(); // 'Secure-password-123!' +``` + +## Notes + +- This class validates password shape only. +- It does not hash, store, or encrypt passwords by itself. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/positive-number.md b/docs/reference/positive-number.md new file mode 100644 index 0000000..9c9e880 --- /dev/null +++ b/docs/reference/positive-number.md @@ -0,0 +1,58 @@ +--- +title: PositiveNumber +description: Number value object constrained to non-negative values. +--- + +# `PositiveNumber` + +Number value object constrained to non-negative values. + +## Import + +```typescript +import { PositiveNumber } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class PositiveNumber extends NumberValueObject +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +The current implementation accepts values greater than or equal to `0`. + +## Throws + +This class can throw: + +- `InvalidPositiveNumberError` +- `InvalidNumberError` + +## Example + +```typescript +import { PositiveNumber } from '@haskou/value-objects'; + +const amount = new PositiveNumber(0); +const price = new PositiveNumber(19.99); + +price.isGreaterThan(amount); // true +``` + +## Notes + +- Despite the class name, the current implementation accepts `0`. +- Arithmetic methods are inherited from `NumberValueObject`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/private-key.md b/docs/reference/private-key.md new file mode 100644 index 0000000..cb239d5 --- /dev/null +++ b/docs/reference/private-key.md @@ -0,0 +1,68 @@ +--- +title: PrivateKey +description: Ed25519 private key in PEM PKCS8 format. +--- + +# `PrivateKey` + +Ed25519 private key in PEM PKCS8 format. + +## Import + +```typescript +import { PrivateKey } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class PrivateKey extends Key +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be a 119-character PEM private key matching the package pattern. + +## Throws + +This class can throw: + +- `InvalidLengthError` +- `InvalidFormatError` + +## Methods + +| Method | Description | +| --- | --- | +| `static fromPEM(pem)` | Creates a private key from PEM. | +| `static generate()` | Generates a new private key. | +| `getPublicKey()` | Derives the matching `PublicKey`. | +| `sign(payload)` | Signs a string-like payload or `Media` and returns `Signature`. | +| `decrypt(encryptedPayload)` | Decrypts asymmetric encrypted payloads addressed to the matching key. | + +## Example + +```typescript +import { PrivateKey } from '@haskou/value-objects'; + +const privateKey = PrivateKey.generate(); +const publicKey = privateKey.getPublicKey(); +const signature = privateKey.sign('hello'); + +publicKey.isValidSignature('hello', signature); // true +``` + +## Notes + +- Payload decryption supports the current v2 asymmetric payload format and the legacy four-part format. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/public-key.md b/docs/reference/public-key.md new file mode 100644 index 0000000..f2e775e --- /dev/null +++ b/docs/reference/public-key.md @@ -0,0 +1,66 @@ +--- +title: PublicKey +description: Ed25519 public key in PEM SPKI format. +--- + +# `PublicKey` + +Ed25519 public key in PEM SPKI format. + +## Import + +```typescript +import { PublicKey } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class PublicKey extends Key +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be a 113-character PEM public key matching the package pattern. + +## Throws + +This class can throw: + +- `InvalidLengthError` +- `InvalidFormatError` + +## Methods + +| Method | Description | +| --- | --- | +| `static fromPEM(pem)` | Creates a public key from PEM. | +| `isValidSignature(payload, signature)` | Verifies a signature for a payload. | +| `encrypt(payload)` | Encrypts payload data and returns `AsymmetricEncryptedPayload`. | + +## Example + +```typescript +import { PrivateKey } from '@haskou/value-objects'; + +const privateKey = PrivateKey.generate(); +const publicKey = privateKey.getPublicKey(); +const payload = publicKey.encrypt('secret'); + +privateKey.decrypt(payload).toString(); // 'secret' +``` + +## Notes + +- Asymmetric encryption is capped at 1 MiB before encryption. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/sha256-hash.md b/docs/reference/sha256-hash.md new file mode 100644 index 0000000..025b4cc --- /dev/null +++ b/docs/reference/sha256-hash.md @@ -0,0 +1,62 @@ +--- +title: SHA256Hash +description: SHA-256 hash value object. +--- + +# `SHA256Hash` + +SHA-256 hash value object. + +## Import + +```typescript +import { SHA256Hash } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class SHA256Hash extends ValueObject +``` + +## Constructor + +```typescript +constructor(source: string | StringValueObject) +``` + +## Validation + +Must be a 64-character hex SHA-256 hash. + +## Throws + +This class can throw: + +- `InvalidHashError` + +## Methods + +| Method | Description | +| --- | --- | +| `static isValid(hash)` | Returns true when the value matches the SHA-256 hash pattern. | +| `static from(buffer)` | Hashes a string, `StringValueObject`, `Media`, or `Buffer`. | +| `toBase64()` | Converts the hex hash to a Base64 `StringValueObject`. | + +## Example + +```typescript +import { SHA256Hash } from '@haskou/value-objects'; + +const hash = SHA256Hash.from('hello'); +hash.toString().length; // 64 +``` + +## Notes + +- Validation is case-insensitive. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/sha512-hash.md b/docs/reference/sha512-hash.md new file mode 100644 index 0000000..ea5b00e --- /dev/null +++ b/docs/reference/sha512-hash.md @@ -0,0 +1,62 @@ +--- +title: SHA512Hash +description: SHA-512 hash value object. +--- + +# `SHA512Hash` + +SHA-512 hash value object. + +## Import + +```typescript +import { SHA512Hash } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class SHA512Hash extends ValueObject +``` + +## Constructor + +```typescript +constructor(source: string | StringValueObject) +``` + +## Validation + +Must be a 128-character hex SHA-512 hash. + +## Throws + +This class can throw: + +- `InvalidHashError` + +## Methods + +| Method | Description | +| --- | --- | +| `static isValid(hash)` | Returns true when the value matches the SHA-512 hash pattern. | +| `static from(buffer)` | Hashes a string, `StringValueObject`, `Media`, or `Buffer`. | +| `toBase64()` | Converts the hex hash to a Base64 `StringValueObject`. | + +## Example + +```typescript +import { SHA512Hash } from '@haskou/value-objects'; + +const hash = SHA512Hash.from('hello'); +hash.toString().length; // 128 +``` + +## Notes + +- Validation is case-insensitive. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/short-id.md b/docs/reference/short-id.md new file mode 100644 index 0000000..84ada96 --- /dev/null +++ b/docs/reference/short-id.md @@ -0,0 +1,64 @@ +--- +title: ShortId +description: MongoDB ObjectId-like 24-character identifier. +--- + +# `ShortId` + +MongoDB ObjectId-like 24-character identifier. + +## Import + +```typescript +import { ShortId } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class ShortId extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be exactly 24 alphanumeric characters according to the current pattern. + +## Throws + +This class can throw: + +- `InvalidLengthError` +- `InvalidFormatError` + +## Methods + +| Method | Description | +| --- | --- | +| `static generate()` | Generates a 24-character hex string using random bytes with a timestamp prefix. | + +## Example + +```typescript +import { ShortId } from '@haskou/value-objects'; + +const id = ShortId.generate(); +id.toString(); // e.g. '69ad70897364ee0d1406b1d0' + +const existing = new ShortId('507f1f77bcf86cd799439011'); +``` + +## Notes + +- Generation returns lowercase hex. +- Constructor validation currently allows alphanumeric characters, not only lowercase hex. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/signature.md b/docs/reference/signature.md new file mode 100644 index 0000000..e075c27 --- /dev/null +++ b/docs/reference/signature.md @@ -0,0 +1,62 @@ +--- +title: Signature +description: Base64 encoded Ed25519 signature value object. +--- + +# `Signature` + +Base64 encoded Ed25519 signature value object. + +## Import + +```typescript +import { Signature } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Signature extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must match the package Base64 signature pattern and be 88 characters long. + +## Throws + +This class can throw: + +- `InvalidSignatureError` + +## Methods + +| Method | Description | +| --- | --- | +| `static fromBuffer(buffer)` | Creates a signature from raw signature bytes. | + +## Example + +```typescript +import { PrivateKey } from '@haskou/value-objects'; + +const privateKey = PrivateKey.generate(); +const signature = privateKey.sign('payload'); + +signature.valueOf().length; // 88 +``` + +## Notes + +- Normally created through `PrivateKey.sign()` or `KeyPair.sign()`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/string-value-object.md b/docs/reference/string-value-object.md new file mode 100644 index 0000000..d4b0690 --- /dev/null +++ b/docs/reference/string-value-object.md @@ -0,0 +1,64 @@ +--- +title: StringValueObject +description: Immutable string wrapper with length validation. +--- + +# `StringValueObject` + +Immutable string wrapper with length validation. + +## Import + +```typescript +import { StringValueObject } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class StringValueObject extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject, maxLength = 512) +``` + +## Validation + +The string length must be less than or equal to `maxLength`. + +## Throws + +This class can throw: + +- `InvalidStringLengthError` + +## Methods + +| Method | Description | +| --- | --- | +| `isEmpty()` | Returns true when the wrapped string is empty. | + +## Example + +```typescript +import { StringValueObject } from '@haskou/value-objects'; + +const title = new StringValueObject('hello'); +const code = new StringValueObject('ABC', 3); + +title.isEmpty(); // false +code.valueOf(); // 'ABC' +``` + +## Notes + +- Passing another `StringValueObject` copies its primitive value. +- The default maximum length is 512 characters. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/symmetric-encrypted-payload.md b/docs/reference/symmetric-encrypted-payload.md new file mode 100644 index 0000000..90c9c8e --- /dev/null +++ b/docs/reference/symmetric-encrypted-payload.md @@ -0,0 +1,56 @@ +--- +title: SymmetricEncryptedPayload +description: Encrypted payload produced by `SymmetricKey`. +--- + +# `SymmetricEncryptedPayload` + +Encrypted payload produced by `SymmetricKey`. + +## Import + +```typescript +import { SymmetricEncryptedPayload } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class SymmetricEncryptedPayload extends EncryptedPayload +``` + +## Constructor + +```typescript +constructor(value: string) +``` + +## Validation + +Created by `SymmetricKey.encrypt()`. Recognized as symmetric by `getScheme()`. + +## Methods + +| Method | Description | +| --- | --- | +| `getScheme()` | Returns `'symmetric'`. | + +## Example + +```typescript +import { SymmetricKey } from '@haskou/value-objects'; + +const key = SymmetricKey.generate(); +const encrypted = key.encrypt('secret'); + +encrypted.getScheme(); // 'symmetric' +``` + +## Notes + +- Current format starts with `v1.aes-256-gcm`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/symmetric-key.md b/docs/reference/symmetric-key.md new file mode 100644 index 0000000..44350c6 --- /dev/null +++ b/docs/reference/symmetric-key.md @@ -0,0 +1,76 @@ +--- +title: SymmetricKey +description: AES-256-GCM symmetric key value object. +--- + +# `SymmetricKey` + +AES-256-GCM symmetric key value object. + +## Import + +```typescript +import { SymmetricKey } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class SymmetricKey extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be strict Base64 and decode to exactly 32 bytes. + +## Throws + +This class can throw: + +- `InvalidFormatError` +- `InvalidLengthError` + +## Methods + +| Method | Description | +| --- | --- | +| `static fromBase64(key)` | Creates from Base64. | +| `static fromBuffer(key)` | Creates from a 32-byte Buffer. | +| `static generate()` | Creates a random 32-byte key. | +| `static fromPassword(password, options)` | Derives a key with scrypt using explicit salt and optional parameters. | +| `static fromPasswordUsingOwasp(password, options)` | Derives using the package OWASP-aligned scrypt profile. | +| `getBuffer()` | Returns the key as a Buffer. | +| `encrypt(payload, options?)` | Encrypts with AES-256-GCM and returns `SymmetricEncryptedPayload`. | +| `decrypt(encryptedPayload, options?)` | Decrypts and returns a Buffer. | + +## Example + +```typescript +import { Password, SymmetricKey } from '@haskou/value-objects'; + +const key = SymmetricKey.generate(); +const encrypted = key.encrypt('secret'); + +key.decrypt(encrypted).toString(); // 'secret' + +const derived = await SymmetricKey.fromPasswordUsingOwasp( + new Password('Secure-password-123!'), + { salt: 'application-specific-salt' }, +); +``` + +## Notes + +- Payload encryption is capped at 8 MiB before encryption. +- Password-derived keys require callers to store or reproduce the salt. The salt is not embedded in `SymmetricEncryptedPayload`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/timestamp-interval.md b/docs/reference/timestamp-interval.md new file mode 100644 index 0000000..2ba6bc0 --- /dev/null +++ b/docs/reference/timestamp-interval.md @@ -0,0 +1,74 @@ +--- +title: TimestampInterval +description: Start/end timestamp interval value object. +--- + +# `TimestampInterval` + +Start/end timestamp interval value object. + +## Import + +```typescript +import { TimestampInterval } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class TimestampInterval extends ValueObject +``` + +## Constructor + +```typescript +constructor(start: Timestamp, end: Timestamp) +``` + +## Validation + +`start` must be before or equal to `end` in the constructor. `modifyStart()` and `modifyEnd()` require strict before. + +## Throws + +This class can throw: + +- `InvalidTimestampIntervalError` + +## Methods + +| Method | Description | +| --- | --- | +| `static fromPrimitives(primitives)` | Restores from `{ start, end }` milliseconds. | +| `toPrimitives()` | Returns `{ start, end }`. | +| `getStart()` | Returns the current start timestamp. | +| `getEnd()` | Returns the current end timestamp. | +| `getDuration()` | Returns a `Duration`. | +| `getTotalDaysOfWeek(dayOfWeek)` | Counts occurrences of a weekday within the interval. | +| `modifyStart(start)` | Mutates the start timestamp after validation. | +| `modifyEnd(end)` | Mutates the end timestamp after validation. | +| `getDaysBetweenInterval()` | Returns all `CalendarDay` values between start and end, inclusive. | +| `getOverlappingInterval(searchInterval)` | Returns overlapping interval or `null`. | +| `includes(timestamp)` | Checks whether a timestamp is inside the interval, inclusive. | + +## Example + +```typescript +import { Timestamp, TimestampInterval } from '@haskou/value-objects'; + +const start = new Timestamp('2026-06-01T00:00:00Z'); +const end = new Timestamp('2026-06-03T00:00:00Z'); +const interval = new TimestampInterval(start, end); + +interval.getDuration().getTotalDays().valueOf(); // 2 +interval.includes(start); // true +``` + +## Notes + +- Unlike most value objects, `modifyStart()` and `modifyEnd()` mutate internal state. Use deliberately, because apparently time was not annoying enough already. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/timestamp.md b/docs/reference/timestamp.md new file mode 100644 index 0000000..eb08d98 --- /dev/null +++ b/docs/reference/timestamp.md @@ -0,0 +1,89 @@ +--- +title: Timestamp +description: UTC timestamp value object stored as milliseconds. +--- + +# `Timestamp` + +UTC timestamp value object stored as milliseconds. + +## Import + +```typescript +import { Timestamp } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Timestamp extends ValueObject +``` + +## Constructor + +```typescript +constructor(value?: number | Date | Timestamp | string) +``` + +## Validation + +String values are parsed with `new Date(value)`. Invalid dates produce `NaN`; this class itself does not reject `NaN`. + +## Methods + +| Method | Description | +| --- | --- | +| `static new(value)` | Creates a new `Timestamp`. | +| `static now()` | Creates a timestamp for the current time. | +| `static fromSeconds(seconds)` | Creates from seconds. | +| `toExactHour()` | Returns a timestamp rounded down to the exact UTC hour. | +| `toMilliseconds()` | Returns milliseconds. | +| `toSeconds()` | Returns rounded seconds. | +| `toDate()` | Returns a `Date`. | +| `isBefore(other)` | Compares milliseconds. | +| `isAfter(other)` | Compares milliseconds. | +| `isBeforeOrEqual(other)` | Compares milliseconds. | +| `isAfterOrEqual(other)` | Compares milliseconds. | +| `addMilliseconds(value)` | Returns a new timestamp. | +| `addSeconds(value)` | Returns a new timestamp. | +| `addMinutes(value)` | Returns a new timestamp. | +| `addHours(value)` | Returns a new timestamp. | +| `addDays(value)` | Returns a new timestamp. | +| `addWeeks(value)` | Returns a new timestamp. | +| `addMonths(value)` | Uses a fixed 30-day month factor. | +| `addYears(value)` | Uses a fixed 365-day year factor. | +| `addDuration(duration)` | Adds a `Duration`. | +| `isSameDay(other)` | Compares `CalendarDay`. | +| `isSameMonth(other)` | Compares `MonthOfYear`. | +| `isSameYear(other)` | Compares `Year`. | +| `getCalendarDay()` | Returns a `CalendarDay`. | +| `getDay()` | Returns a UTC `Day`. | +| `getMonth()` | Returns a UTC `Month`. | +| `getYear()` | Returns a UTC `Year`. | +| `getHours()` | Returns UTC hours. | +| `getMinutes()` | Returns UTC minutes. | +| `getSeconds()` | Returns UTC seconds. | +| `getMilliseconds()` | Returns UTC milliseconds. | +| `getDayOfWeek()` | Returns JavaScript UTC day number. | +| `getMonthOfYear()` | Returns `MonthOfYear`. | + +## Example + +```typescript +import { Timestamp } from '@haskou/value-objects'; + +const timestamp = new Timestamp('2026-06-23T10:00:00.000Z'); + +timestamp.addHours(2).toDate().toISOString(); // '2026-06-23T12:00:00.000Z' +timestamp.getYear().valueOf(); // 2026 +``` + +## Notes + +- All date component getters use UTC. +- `addMonths()` and `addYears()` use fixed duration factors, not calendar arithmetic. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/unique-object-array.md b/docs/reference/unique-object-array.md new file mode 100644 index 0000000..26e1a54 --- /dev/null +++ b/docs/reference/unique-object-array.md @@ -0,0 +1,66 @@ +--- +title: UniqueObjectArray +description: Iterable collection that keeps comparable items unique. +--- + +# `UniqueObjectArray` + +Iterable collection that keeps comparable items unique. + +## Import + +```typescript +import { UniqueObjectArray } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class UniqueObjectArray implements Iterable +``` + +## Constructor + +```typescript +constructor() +``` + +## Validation + +Uniqueness is based on each item's `isEqual()` method. + +## Methods + +| Method | Description | +| --- | --- | +| `static fromArray(array)` | Builds a unique collection from an array. | +| `includes(item)` | Returns true when an equal item exists. | +| `push(item)` | Adds the item when no equal item exists. Returns true when inserted. | +| `remove(item)` | Removes the equal item. Returns true when removed. | +| `length()` | Returns the number of stored items. | +| `toArray()` | Returns a shallow array copy. | +| `[Symbol.iterator]()` | Iterates over stored items. | + +## Example + +```typescript +import { DayOfWeek, UniqueObjectArray } from '@haskou/value-objects'; + +const days = UniqueObjectArray.fromArray([ + DayOfWeek.MONDAY, + DayOfWeek.TUESDAY, + DayOfWeek.MONDAY, +]); + +days.length(); // 2 +[...days].map((day) => day.toString()); // ['monday', 'tuesday'] +``` + +## Notes + +- Items must implement `isEqual(item: unknown): boolean`. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/uuid.md b/docs/reference/uuid.md new file mode 100644 index 0000000..372ed11 --- /dev/null +++ b/docs/reference/uuid.md @@ -0,0 +1,61 @@ +--- +title: UUID +description: UUID value object with random generation. +--- + +# `UUID` + +UUID value object with random generation. + +## Import + +```typescript +import { UUID } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class UUID extends ValueObject +``` + +## Constructor + +```typescript +constructor(value: string | StringValueObject) +``` + +## Validation + +Must be exactly 36 characters and match the current lowercase alphanumeric/hyphen pattern. + +## Throws + +This class can throw: + +- `InvalidLengthError` +- `InvalidFormatError` + +## Methods + +| Method | Description | +| --- | --- | +| `static generate()` | Generates a random UUID-like v4 value. | + +## Example + +```typescript +import { UUID } from '@haskou/value-objects'; + +const id = UUID.generate(); +id.toString(); // e.g. '3fd4c04a-8e73-4e10-aef3-f491b32ec538' +``` + +## Notes + +- The constructor pattern accepts lowercase `a-z`, `0-9`, and hyphens. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/value-object.md b/docs/reference/value-object.md new file mode 100644 index 0000000..78513e4 --- /dev/null +++ b/docs/reference/value-object.md @@ -0,0 +1,62 @@ +--- +title: ValueObject +description: Base class for primitive value wrappers. +--- + +# `ValueObject` + +Base class for primitive value wrappers. + +## Import + +```typescript +import { ValueObject } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +abstract class ValueObject +``` + +## Constructor + +```typescript +constructor(value: T | null | undefined) +``` + +## Validation + +Returns a null object when the constructor value is `null` or `undefined`; otherwise stores the primitive value. + +## Methods + +| Method | Description | +| --- | --- | +| `valueOf()` | Returns the wrapped primitive value. | +| `toString()` | Returns `valueOf().toString()`. | +| `isEqual(other)` | Compares by primitive value using `other?.valueOf()`. | +| `isNotEqual(other)` | Negates `isEqual()`. | +| `clone(value)` | Protected helper used by subclasses to return a new instance of the current class. | + +## Example + +```typescript +import { ValueObject } from '@haskou/value-objects'; + +class UserName extends ValueObject {} + +const name = new UserName('hasko'); +name.valueOf(); // 'hasko' +name.isEqual('hasko'); // true +``` + +## Notes + +- Use `ValueObject` when no extra validation is needed beyond null-object handling. +- Most concrete classes in this package extend it directly or indirectly. + +## Related + +- [Error handling](/guides/error-handling) +- [Reference overview](/reference/) diff --git a/docs/reference/year.md b/docs/reference/year.md new file mode 100644 index 0000000..395d426 --- /dev/null +++ b/docs/reference/year.md @@ -0,0 +1,53 @@ +--- +title: Year +description: Year value object with leap-year helpers. +--- + +# `Year` + +Integer year value object with leap-year helpers. + +## Import + +```typescript +import { Year } from '@haskou/value-objects'; +``` + +## Signature + +```typescript +class Year extends Integer +``` + +## Constructor + +```typescript +constructor(value: number | NumberValueObject) +``` + +## Validation + +Uses `Integer` validation. The current implementation accepts negative years and zero because no calendar range is enforced. + +## Methods + +| Method | Description | +| --- | --- | +| `isLeapYear()` | Returns true when `getNumberOfDays()` is 366. | +| `getNumberOfDays()` | Returns 365 or 366 using Gregorian leap-year rules. | + +## Example + +```typescript +import { Year } from '@haskou/value-objects'; + +const year = new Year(2024); + +year.isLeapYear(); // true +year.getNumberOfDays(); // 366 +``` + +## Notes + +- Years divisible by 4 are leap years unless divisible by 100, except years divisible by 400. +- `Year` inherits number comparison helpers from `Integer` and `NumberValueObject`. diff --git a/package.json b/package.json index dfd40ae..16ea405 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "@haskou/value-objects", "version": "2.10.1", + "description": "TypeScript value objects for validated, immutable primitive values and small domain-safe data structures.", "main": "dist/index.js", "types": "dist/index.d.ts", "exports": { @@ -19,6 +20,28 @@ "engines": { "node": ">=20.19.0" }, + "keywords": [ + "value-object", + "value-objects", + "typescript", + "validation", + "validated-types", + "immutable", + "immutability", + "type-safety", + "domain-primitives", + "primitive-obsession", + "ddd", + "domain-driven-design", + "email-validation", + "uuid", + "hash", + "crypto", + "aes-256-gcm", + "ed25519", + "x25519", + "scrypt" + ], "publishConfig": { "access": "public" }, @@ -29,14 +52,17 @@ "bugs": { "url": "https://github.com/haskou/value-objects/issues" }, - "homepage": "https://github.com/haskou/value-objects#readme", + "homepage": "https://haskou.github.io/value-objects/", "scripts": { "lint": "eslint ./src --ext .ts", "lint:fix": "eslint ./src --ext .ts --fix", "test": "yarn lint && yarn test:unit", "test:unit": "jest --runInBand", "test:coverage": "NODE_ENV=test jest --collect-coverage", - "build": "rm -rf dist/ && tsc" + "build": "rm -rf dist/ && tsc", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress build docs && vitepress preview docs" }, "dependencies": { "@noble/ciphers": "^2.2.0", @@ -60,6 +86,8 @@ "jest-extended": "^7.0.0", "prettier": "^3.8.4", "ts-jest": "^29.4.11", - "typescript": "^6.0.3" + "typescript": "^6.0.3", + "vitepress": "^1.6.4", + "vue": "^3.5.22" } } diff --git a/yarn.lock b/yarn.lock index 9feeede..9e3d5f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,159 @@ # yarn lockfile v1 +"@algolia/abtesting@1.21.1": + version "1.21.1" + resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.21.1.tgz#bf5c7881302a5843b78c6f44431d27ba5eb8f179" + integrity sha512-Wia5/mNTfiU0PIUN25UMfAGGdASkkwuCS9nBAdmhqrNPY/ff7U/6MgBVdwFDPsa3sA1msutPtO50gvOzx6MOXA== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/autocomplete-core@1.17.7": + version "1.17.7" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz#2c410baa94a47c5c5f56ed712bb4a00ebe24088b" + integrity sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q== + dependencies: + "@algolia/autocomplete-plugin-algolia-insights" "1.17.7" + "@algolia/autocomplete-shared" "1.17.7" + +"@algolia/autocomplete-plugin-algolia-insights@1.17.7": + version "1.17.7" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz#7d2b105f84e7dd8f0370aa4c4ab3b704e6760d82" + integrity sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A== + dependencies: + "@algolia/autocomplete-shared" "1.17.7" + +"@algolia/autocomplete-preset-algolia@1.17.7": + version "1.17.7" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz#c9badc0d73d62db5bf565d839d94ec0034680ae9" + integrity sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA== + dependencies: + "@algolia/autocomplete-shared" "1.17.7" + +"@algolia/autocomplete-shared@1.17.7": + version "1.17.7" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz#105e84ad9d1a31d3fb86ba20dc890eefe1a313a0" + integrity sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg== + +"@algolia/client-abtesting@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.55.1.tgz#192d0fd57ec9b7d6924a8b19cd98e72c7e8ae6de" + integrity sha512-miW8RzAtBgNiEJ9fGEhsOPgWUpekAe64YcVufqXrlykj0Jjmo5nj0a5f/HAzRVX5ZuU1GAVd7BkzFDx7q50P3A== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/client-analytics@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.55.1.tgz#2e6e14d03f1cb181c2086fd60324c24f5f3007f9" + integrity sha512-eR3J3kB9JX6DdCvDRi3I4KPfwO6fR9HWYRXhVke2TXIoOQafMKCRAneg33JRmIrb+DnnJ/eWApJLF1O1CLPERg== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/client-common@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.55.1.tgz#4e023b4127805f8d2a3229f1711822927f3f4303" + integrity sha512-P5ak7EurwYqgAiDyb95mgA3WRR/Zu8CPMv36lWTISvL2AmlPyqQPy2nX/KEJRTcwaeTWwrk6wJV4/M93GfjOWw== + +"@algolia/client-insights@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.55.1.tgz#f58d7908071e9f5fb328533368e3bb2dad797fe5" + integrity sha512-OVtj9uA//+pjvKQI5INnzbyLrf3ClNv3XRbWswwJ2kHIStQNHtBfHo+LofNB/WhM9xjuXlW5ANn2aMj65UGx7w== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/client-personalization@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.55.1.tgz#1b4d2bd3ba031864b1c8cd3e839ecb81146f8023" + integrity sha512-oKlVFlp+qbIEe4p7E54zSiP2gEV/vDu972Ykv8VDMFwEvreS7m0YKA3a8hGGHwc7yiBUGGiR3LlwzMLfnJmy6Q== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/client-query-suggestions@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.55.1.tgz#7ddb439a51f348711d2b5b7bddef9497e906be86" + integrity sha512-BOVrld6vdtsFmotVDMTVQfYXwrVplJ+DUvy60JFi+tkWV698q2J9NNPKEO3dr5qxtSLKQP4vHF8n+3U5PDWhOQ== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/client-search@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.55.1.tgz#8a918fb875b9f3c8e73b7331e508196a33538e71" + integrity sha512-GAqHl9zERhC3bbBfubwUu07G3UXO06gORvOcsiTBZB3et0s3auNUbHlYdYNp4VKa3sUZqH5AcD3OKzU/KDGXjQ== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/ingestion@1.55.1": + version "1.55.1" + resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.55.1.tgz#036d40d394314d3f94dd1779d33378d8a65f48a5" + integrity sha512-BXZw+C+gsWL7pZvbnhJUnCXASiDLGcQxVV7h55Pyh2DmSzwdZIVccE5xc9RVD2trtrhIqk5smuODTxtaZqd0IA== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/monitoring@1.55.1": + version "1.55.1" + resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.55.1.tgz#307c71309cd52dbcc43d0fbd7f35a698354dd6d8" + integrity sha512-9g/ceZrZTqA62FA3588Xj0onRPjDNfu0pVQqefK0rrHp9H6Wblph/YmzGjZ2g8uqbTh0ZGIvAGCzErU8f7MHpA== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/recommend@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.55.1.tgz#72b5440d6221283acf5dde2828cf442769f5315a" + integrity sha512-cZTIrGyAP+W4A6jDVwvWM/JOaoJKQkD/2a5eLUEeNdKAD45jN7BCpsMDONyhZlosLa4UwL8uiINQzj4iFy9nqg== + dependencies: + "@algolia/client-common" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + +"@algolia/requester-browser-xhr@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.55.1.tgz#6809cfab8aab74f6cb3d69cd4c1e2d2c2be362da" + integrity sha512-N6I3leW0UO8Y9Zv90yo2UHgYGuxZO0mjbvzNxDIJDjO0qECEF7Z9XMvSNeUWXQh/iNDA9lr8MfEy3rmZGIcclw== + dependencies: + "@algolia/client-common" "5.55.1" + +"@algolia/requester-fetch@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.55.1.tgz#f75cc8681524bd8eb60d5d976c31f568dbdbe0ea" + integrity sha512-ukU5zeeFs44rQkzv+TRdYard+d+3lmPGs8lPZhHtWE8rfz+LlBSF6s9kP3VQ7LeOYL8Dz0u6tZfnyTrqrumbHQ== + dependencies: + "@algolia/client-common" "5.55.1" + +"@algolia/requester-node-http@5.55.1": + version "5.55.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.55.1.tgz#1920f83d70be909739dbc4d1421ca241bc0f1e0e" + integrity sha512-lCwXyijwPm3vbYHpBXPRomMcD6mgiptmps27gnMCf4HK+u/AOeFPBnIFh4V3l4A5SnP9VRiKBZqwGBpUH0vaTg== + dependencies: + "@algolia/client-common" "5.55.1" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.29.7": version "7.29.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" @@ -270,6 +423,29 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@docsearch/css@3.8.2": + version "3.8.2" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.8.2.tgz#7973ceb6892c30f154ba254cd05c562257a44977" + integrity sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ== + +"@docsearch/js@3.8.2": + version "3.8.2" + resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.8.2.tgz#bdcfc9837700eb38453b88e211ab5cc5a3813cc6" + integrity sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ== + dependencies: + "@docsearch/react" "3.8.2" + preact "^10.0.0" + +"@docsearch/react@3.8.2": + version "3.8.2" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.8.2.tgz#7b11d39b61c976c0aa9fbde66e6b73b30f3acd42" + integrity sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg== + dependencies: + "@algolia/autocomplete-core" "1.17.7" + "@algolia/autocomplete-preset-algolia" "1.17.7" + "@docsearch/css" "3.8.2" + algoliasearch "^5.14.2" + "@emnapi/core@1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467" @@ -292,6 +468,121 @@ dependencies: tslib "^2.4.0" +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" @@ -371,6 +662,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== +"@iconify-json/simple-icons@^1.2.21": + version "1.2.87" + resolved "https://registry.yarnpkg.com/@iconify-json/simple-icons/-/simple-icons-1.2.87.tgz#f8929f5eb22523d5f8ef68a223f88a3b5c1392e1" + integrity sha512-8YciStObhSji3OZFmWAWK6kBujyqO5bLCxeDwLxf3CR3F4PVelq7keC2LBvgTqviWzSTysj5/g4PCFLiAMVGsw== + dependencies: + "@iconify/types" "*" + +"@iconify/types@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@iconify/types/-/types-2.0.0.tgz#ab0e9ea681d6c8a1214f30cd741fe3a20cc57f57" + integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -639,7 +942,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": version "1.5.5" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== @@ -686,6 +989,195 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.3.6.tgz#3569708bd4be4d8870ba32bf1c456dac81600d97" integrity sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA== +"@rollup/rollup-android-arm-eabi@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz#5e9849b661c2229cf967a08dbe2dbbe9e8c991e5" + integrity sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg== + +"@rollup/rollup-android-arm64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz#5b0699ee5dd484b222c9ed74aff43c91ea8b17f8" + integrity sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw== + +"@rollup/rollup-darwin-arm64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz#8bc52c9d7a3ce8d0533c351a9c935de781daa06f" + integrity sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A== + +"@rollup/rollup-darwin-x64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz#ba2ef3e8fb310f0af35588f270cfa5aa96e48764" + integrity sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA== + +"@rollup/rollup-freebsd-arm64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz#93b10bdbfe8ada226b8bc0c02ef6b7f544474d96" + integrity sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw== + +"@rollup/rollup-freebsd-x64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz#3e8aa38ef3c9c300946871e3fdbb0c30e0a20f86" + integrity sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg== + +"@rollup/rollup-linux-arm-gnueabihf@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz#1d7994384bb0ad1bc41921b506e1642d4f9d7fc3" + integrity sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg== + +"@rollup/rollup-linux-arm-musleabihf@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz#a6540f47cf844a56b80ca9ff95d2acdfb2cef97b" + integrity sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA== + +"@rollup/rollup-linux-arm64-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz#404f2045651840cbf48da91ba6d0f490f0bc2cbf" + integrity sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA== + +"@rollup/rollup-linux-arm64-musl@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz#a3404ffddf7b474b48c99b9c893b6247bb765ba5" + integrity sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ== + +"@rollup/rollup-linux-loong64-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz#e8aac6d549b377945e349882f199b7c8eb75ca38" + integrity sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg== + +"@rollup/rollup-linux-loong64-musl@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz#6e2e44ea50310b3a582078a915e5feb879c820d4" + integrity sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ== + +"@rollup/rollup-linux-ppc64-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz#6898302da6d77a0537cde64b2b4c6b60659bd110" + integrity sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A== + +"@rollup/rollup-linux-ppc64-musl@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz#333717c95dd5a66bef8f63e7ef8a9fd845fd18d0" + integrity sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w== + +"@rollup/rollup-linux-riscv64-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz#81bc06ba380352004d01f4826eb7cdccefa05bad" + integrity sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg== + +"@rollup/rollup-linux-riscv64-musl@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz#95a7cd39de21389ad6788a5284eaaa738e29ca4c" + integrity sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q== + +"@rollup/rollup-linux-s390x-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz#06e6db2ec1bc48b5374c7923ef83c2eb024b2452" + integrity sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg== + +"@rollup/rollup-linux-x64-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz#5dc818988285e09e88790c6462def72413df2da3" + integrity sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A== + +"@rollup/rollup-linux-x64-musl@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz#2080f4a93349e9afd34be6fc1a37e01fc8bfc80f" + integrity sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg== + +"@rollup/rollup-openbsd-x64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz#21d64a8acb66221724b923e51af5333df1af044b" + integrity sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg== + +"@rollup/rollup-openharmony-arm64@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz#8e0fcd9d02141e337b4c5b5cff576cb9a76b1ba0" + integrity sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA== + +"@rollup/rollup-win32-arm64-msvc@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz#bdb4cc4efd58efe808203347f0f5463f0ea16e52" + integrity sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg== + +"@rollup/rollup-win32-ia32-msvc@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz#dbaebde5afd24eae0eefe915d901632e7cb59860" + integrity sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q== + +"@rollup/rollup-win32-x64-gnu@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz#84109e85fea5f8f1353499f96578fdc2a0e8b138" + integrity sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg== + +"@rollup/rollup-win32-x64-msvc@4.62.2": + version "4.62.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz#3671ce3f9b928d5c01f879792d5c0b60ae14d4ad" + integrity sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA== + +"@shikijs/core@2.5.0", "@shikijs/core@^2.1.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-2.5.0.tgz#e14d33961dfa3141393d4a76fc8923d0d1c4b62f" + integrity sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg== + dependencies: + "@shikijs/engine-javascript" "2.5.0" + "@shikijs/engine-oniguruma" "2.5.0" + "@shikijs/types" "2.5.0" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.4" + +"@shikijs/engine-javascript@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz#e045c6ecfbda6c99137547b0a482e0b87f1053fc" + integrity sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w== + dependencies: + "@shikijs/types" "2.5.0" + "@shikijs/vscode-textmate" "^10.0.2" + oniguruma-to-es "^3.1.0" + +"@shikijs/engine-oniguruma@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz#230de5693cc1da6c9d59c7ad83593c2027274817" + integrity sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw== + dependencies: + "@shikijs/types" "2.5.0" + "@shikijs/vscode-textmate" "^10.0.2" + +"@shikijs/langs@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-2.5.0.tgz#97ab50c495922cc1ca06e192985b28dc73de5d50" + integrity sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w== + dependencies: + "@shikijs/types" "2.5.0" + +"@shikijs/themes@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-2.5.0.tgz#8c6aecf73f5455681c8bec15797cf678162896cb" + integrity sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw== + dependencies: + "@shikijs/types" "2.5.0" + +"@shikijs/transformers@^2.1.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/transformers/-/transformers-2.5.0.tgz#190c84786ff06c417580ab79177338a947168c55" + integrity sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg== + dependencies: + "@shikijs/core" "2.5.0" + "@shikijs/types" "2.5.0" + +"@shikijs/types@2.5.0", "@shikijs/types@^2.1.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-2.5.0.tgz#e949c7384802703a48b9d6425dd41673c164df69" + integrity sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw== + dependencies: + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + +"@shikijs/vscode-textmate@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" + integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== + "@sinclair/typebox@^0.34.0": version "0.34.49" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.49.tgz#4f1369234f2ecf693866476c3b2e1b54d2a9d68e" @@ -750,11 +1242,18 @@ resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec" integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== -"@types/estree@^1.0.6", "@types/estree@^1.0.8": +"@types/estree@1.0.9", "@types/estree@^1.0.6", "@types/estree@^1.0.8": version "1.0.9" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24" integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== +"@types/hast@^3.0.0", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.1", "@types/istanbul-lib-coverage@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" @@ -787,6 +1286,31 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/linkify-it@^5": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" + integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== + +"@types/markdown-it@^14.1.2": + version "14.1.2" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61" + integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== + dependencies: + "@types/linkify-it" "^5" + "@types/mdurl" "^2" + +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + +"@types/mdurl@^2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" + integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== + "@types/node@*", "@types/node@^25.9.3": version "25.9.3" resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.3.tgz#11dfe7a33e68fa5c560f0aa76cc5595621ef26b9" @@ -799,6 +1323,16 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/web-bluetooth@^0.0.21": + version "0.0.21" + resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz#525433c784aed9b457aaa0ee3d92aeb71f346b63" + integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -907,6 +1441,11 @@ "@typescript-eslint/types" "8.61.0" eslint-visitor-keys "^5.0.0" +"@ungap/structured-clone@^1.0.0": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.2.tgz#a03ad82cd5676414d068ba86f880c5681194aadf" + integrity sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA== + "@ungap/structured-clone@^1.3.0": version "1.3.1" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.1.tgz#0e8f34854df7966b09304a18e808b23997bb9fc1" @@ -1026,6 +1565,149 @@ resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz#72da0da48d72b1e87831b9c0308931d3f4669027" integrity sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA== +"@vitejs/plugin-vue@^5.2.1": + version "5.2.4" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz#9e8a512eb174bfc2a333ba959bbf9de428d89ad8" + integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA== + +"@vue/compiler-core@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.38.tgz#fb6679d50a5de198398a1df2ce9d3a49ef8e8072" + integrity sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ== + dependencies: + "@babel/parser" "^7.29.7" + "@vue/shared" "3.5.38" + entities "^7.0.1" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + +"@vue/compiler-dom@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz#10ad73a70399a4c09dedf45ff6a93d42eadc861f" + integrity sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw== + dependencies: + "@vue/compiler-core" "3.5.38" + "@vue/shared" "3.5.38" + +"@vue/compiler-sfc@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz#2b02036f89c7db0a688534a2eb0d42728c7f09c1" + integrity sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg== + dependencies: + "@babel/parser" "^7.29.7" + "@vue/compiler-core" "3.5.38" + "@vue/compiler-dom" "3.5.38" + "@vue/compiler-ssr" "3.5.38" + "@vue/shared" "3.5.38" + estree-walker "^2.0.2" + magic-string "^0.30.21" + postcss "^8.5.15" + source-map-js "^1.2.1" + +"@vue/compiler-ssr@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz#23975aa9643752c78c0625277ffa86f58600300d" + integrity sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA== + dependencies: + "@vue/compiler-dom" "3.5.38" + "@vue/shared" "3.5.38" + +"@vue/devtools-api@^7.7.0": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz#999dbea50da6b00cf59a1336f11fdc2b43d9e063" + integrity sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g== + dependencies: + "@vue/devtools-kit" "^7.7.9" + +"@vue/devtools-kit@^7.7.9": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz#bc218a815616e8987df7ab3e10fc1fb3b8706c58" + integrity sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA== + dependencies: + "@vue/devtools-shared" "^7.7.9" + birpc "^2.3.0" + hookable "^5.5.3" + mitt "^3.0.1" + perfect-debounce "^1.0.0" + speakingurl "^14.0.1" + superjson "^2.2.2" + +"@vue/devtools-shared@^7.7.9": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz#fa4c096b744927081a7dda5fcf05f34b1ae6ca14" + integrity sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA== + dependencies: + rfdc "^1.4.1" + +"@vue/reactivity@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.38.tgz#71cf88b764a6d5334dd27d5ac9f029c81cd4fc34" + integrity sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ== + dependencies: + "@vue/shared" "3.5.38" + +"@vue/runtime-core@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.38.tgz#26560a876b9c0251e57f7a05f9263e623f244f9c" + integrity sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw== + dependencies: + "@vue/reactivity" "3.5.38" + "@vue/shared" "3.5.38" + +"@vue/runtime-dom@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz#7aec70bd013e43166e9ccecd4a1f096fd8ca11fb" + integrity sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A== + dependencies: + "@vue/reactivity" "3.5.38" + "@vue/runtime-core" "3.5.38" + "@vue/shared" "3.5.38" + csstype "^3.2.3" + +"@vue/server-renderer@3.5.38": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.38.tgz#5d24a5305e776a53038fc772fbd9d1205c236588" + integrity sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw== + dependencies: + "@vue/compiler-ssr" "3.5.38" + "@vue/shared" "3.5.38" + +"@vue/shared@3.5.38", "@vue/shared@^3.5.13": + version "3.5.38" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.38.tgz#6c4be4defd86cdc35bfa2b7dd45d7b11a5d51101" + integrity sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug== + +"@vueuse/core@12.8.2", "@vueuse/core@^12.4.0": + version "12.8.2" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-12.8.2.tgz#007c6dd29a7d1f6933e916e7a2f8ef3c3f968eaa" + integrity sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ== + dependencies: + "@types/web-bluetooth" "^0.0.21" + "@vueuse/metadata" "12.8.2" + "@vueuse/shared" "12.8.2" + vue "^3.5.13" + +"@vueuse/integrations@^12.4.0": + version "12.8.2" + resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-12.8.2.tgz#d04f33d86fe985c9a27c98addcfde9f30f2db1df" + integrity sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g== + dependencies: + "@vueuse/core" "12.8.2" + "@vueuse/shared" "12.8.2" + vue "^3.5.13" + +"@vueuse/metadata@12.8.2": + version "12.8.2" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-12.8.2.tgz#6cb3a4e97cdcf528329eebc1bda73cd7f64318d3" + integrity sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A== + +"@vueuse/shared@12.8.2": + version "12.8.2" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-12.8.2.tgz#b9e4611d0603629c8e151f982459da394e22f930" + integrity sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w== + dependencies: + vue "^3.5.13" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1046,6 +1728,26 @@ ajv@^6.14.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +algoliasearch@^5.14.2: + version "5.55.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.55.1.tgz#d9c8a4636c79946881802a0d7426fc33333873e1" + integrity sha512-FyaFnnsbVPtevQwqSj/SdxE3jAsSsY0BEH8IVLf9rXxEBdAhAmT6VKCVSMWoaPIHVN1Eufh/1w8q6k8URpIkWw== + dependencies: + "@algolia/abtesting" "1.21.1" + "@algolia/client-abtesting" "5.55.1" + "@algolia/client-analytics" "5.55.1" + "@algolia/client-common" "5.55.1" + "@algolia/client-insights" "5.55.1" + "@algolia/client-personalization" "5.55.1" + "@algolia/client-query-suggestions" "5.55.1" + "@algolia/client-search" "5.55.1" + "@algolia/ingestion" "1.55.1" + "@algolia/monitoring" "1.55.1" + "@algolia/recommend" "5.55.1" + "@algolia/requester-browser-xhr" "5.55.1" + "@algolia/requester-fetch" "5.55.1" + "@algolia/requester-node-http" "5.55.1" + ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -1175,6 +1877,11 @@ baseline-browser-mapping@^2.10.12: resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.36.tgz#ff3c7c87095c70075b1ac787160fd4fc98750a74" integrity sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg== +birpc@^2.3.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/birpc/-/birpc-2.9.0.tgz#b59550897e4cd96a223e2a6c1475b572236ed145" + integrity sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw== + brace-expansion@^1.1.7: version "1.1.15" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.15.tgz#a6d90d54067236e5f42570a3b7378d594d9b7738" @@ -1265,6 +1972,11 @@ caniuse-lite@^1.0.30001782: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz#5c909138c27f1a61219d3e092071c1cc7d32dc55" integrity sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw== +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -1278,6 +1990,16 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + ci-info@^4.2.0: version "4.4.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.4.0.tgz#7d54eff9f54b45b62401c26032696eb59c8bd18c" @@ -1319,6 +2041,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1329,6 +2056,13 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +copy-anything@^4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-4.0.5.tgz#16cabafd1ea4bb327a540b750f2b4df522825aea" + integrity sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA== + dependencies: + is-what "^5.2.0" + cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -1338,6 +2072,11 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +csstype@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" @@ -1360,11 +2099,23 @@ deepmerge@^4.3.1: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + detect-newline@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -1380,6 +2131,11 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emoji-regex-xs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz#e8af22e5d9dbd7f7f22d280af3d19d2aab5b0724" + integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1390,6 +2146,11 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +entities@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + error-ex@^1.3.1: version "1.3.4" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" @@ -1397,6 +2158,35 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -1545,6 +2335,11 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -1650,6 +2445,13 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== +focus-trap@^7.6.4: + version "7.8.0" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.8.0.tgz#b1d9463fa42b93ad7a5223d750493a6c09b672a8" + integrity sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA== + dependencies: + tabbable "^6.4.0" + foreground-child@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" @@ -1663,7 +2465,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.3: +fsevents@^2.3.3, fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -1751,11 +2553,45 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +hast-util-to-html@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + +hookable@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" + integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -1834,6 +2670,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-what@^5.2.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-5.5.0.tgz#a3031815757cfe1f03fed990bf6355a2d3f628c4" + integrity sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2362,6 +3203,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + make-dir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -2381,11 +3229,63 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +mark.js@8.11.1: + version "8.11.1" + resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5" + integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== + +mdast-util-to-hast@^13.0.0: + version "13.2.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz#d7ff84ca499a57e2c060ae67548ad950e689a053" + integrity sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -2422,11 +3322,26 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== +minisearch@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/minisearch/-/minisearch-7.2.0.tgz#3dc30e41e9464b3836553b6d969b656614f8f359" + integrity sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg== + +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.3.12: + version "3.3.15" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.15.tgz#36c490fad8c6e86c824c940dfdde999b69ed4316" + integrity sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA== + napi-postinstall@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" @@ -2483,6 +3398,15 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +oniguruma-to-es@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz#480e4bac4d3bc9439ac0d2124f0725e7a0d76d17" + integrity sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ== + dependencies: + emoji-regex-xs "^1.0.0" + regex "^6.0.1" + regex-recursion "^6.0.2" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -2566,6 +3490,11 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -2593,6 +3522,20 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +postcss@^8.4.43, postcss@^8.5.15: + version "8.5.15" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.15.tgz#d1eaf677a324e9ec02196da2d3fecf4a0b9a735c" + integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A== + dependencies: + nanoid "^3.3.12" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +preact@^10.0.0: + version "10.29.2" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.29.2.tgz#3e6069c471718b8d124d1cd67565114532e88d70" + integrity sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -2620,6 +3563,11 @@ pretty-format@30.4.1, pretty-format@^30.0.0: react-is-18 "npm:react-is@^18.3.1" react-is-19 "npm:react-is@^19.2.5" +property-information@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.2.0.tgz#0809b34264e995c0bfcd3227028a1e35210af80a" + integrity sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -2647,6 +3595,25 @@ refa@^0.12.0, refa@^0.12.1: dependencies: "@eslint-community/regexpp" "^4.8.0" +regex-recursion@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-6.0.2.tgz#a0b1977a74c87f073377b938dbedfab2ea582b33" + integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== + dependencies: + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/regex/-/regex-6.1.0.tgz#d7ce98f8ee32da7497c13f6601fca2bc4a6a7803" + integrity sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg== + dependencies: + regex-utilities "^2.3.0" + regexp-ast-analysis@^0.7.0: version "0.7.1" resolved "https://registry.yarnpkg.com/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz#c0e24cb2a90f6eadd4cbaaba129317e29d29c482" @@ -2672,6 +3639,45 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rollup@^4.20.0: + version "4.62.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.62.2.tgz#d90fc4cb811f071303c890b779595634f35f9541" + integrity sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA== + dependencies: + "@types/estree" "1.0.9" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.62.2" + "@rollup/rollup-android-arm64" "4.62.2" + "@rollup/rollup-darwin-arm64" "4.62.2" + "@rollup/rollup-darwin-x64" "4.62.2" + "@rollup/rollup-freebsd-arm64" "4.62.2" + "@rollup/rollup-freebsd-x64" "4.62.2" + "@rollup/rollup-linux-arm-gnueabihf" "4.62.2" + "@rollup/rollup-linux-arm-musleabihf" "4.62.2" + "@rollup/rollup-linux-arm64-gnu" "4.62.2" + "@rollup/rollup-linux-arm64-musl" "4.62.2" + "@rollup/rollup-linux-loong64-gnu" "4.62.2" + "@rollup/rollup-linux-loong64-musl" "4.62.2" + "@rollup/rollup-linux-ppc64-gnu" "4.62.2" + "@rollup/rollup-linux-ppc64-musl" "4.62.2" + "@rollup/rollup-linux-riscv64-gnu" "4.62.2" + "@rollup/rollup-linux-riscv64-musl" "4.62.2" + "@rollup/rollup-linux-s390x-gnu" "4.62.2" + "@rollup/rollup-linux-x64-gnu" "4.62.2" + "@rollup/rollup-linux-x64-musl" "4.62.2" + "@rollup/rollup-openbsd-x64" "4.62.2" + "@rollup/rollup-openharmony-arm64" "4.62.2" + "@rollup/rollup-win32-arm64-msvc" "4.62.2" + "@rollup/rollup-win32-ia32-msvc" "4.62.2" + "@rollup/rollup-win32-x64-gnu" "4.62.2" + "@rollup/rollup-win32-x64-msvc" "4.62.2" + fsevents "~2.3.2" + scslre@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/scslre/-/scslre-0.3.0.tgz#c3211e9bfc5547fc86b1eabaa34ed1a657060155" @@ -2703,6 +3709,20 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shiki@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-2.5.0.tgz#09d01ebf3b0b06580431ce3ddc023320442cf223" + integrity sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ== + dependencies: + "@shikijs/core" "2.5.0" + "@shikijs/engine-javascript" "2.5.0" + "@shikijs/engine-oniguruma" "2.5.0" + "@shikijs/langs" "2.5.0" + "@shikijs/themes" "2.5.0" + "@shikijs/types" "2.5.0" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -2718,6 +3738,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -2731,6 +3756,16 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + +speakingurl@^14.0.1: + version "14.0.1" + resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53" + integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -2778,6 +3813,14 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2814,6 +3857,13 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +superjson@^2.2.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/superjson/-/superjson-2.2.6.tgz#a223a3a988172a5f9656e2063fe5f733af40d099" + integrity sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA== + dependencies: + copy-anything "^4" + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -2835,6 +3885,11 @@ synckit@^0.11.13, synckit@^0.11.8: dependencies: "@pkgr/core" "^0.3.6" +tabbable@^6.4.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.5.0.tgz#a65101385a4fd6cbd580b7546da0170f307b535d" + integrity sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA== + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -2857,6 +3912,11 @@ tmpl@1.0.5: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + ts-api-utils@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" @@ -2919,6 +3979,44 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91" integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg== +unist-util-is@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.1.tgz#d0a3f86f2dd0db7acd7d8c2478080b5c67f9c6a9" + integrity sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz#777df7fb98652ce16b4b7cd999d0a1a40efa3a02" + integrity sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz#9a2a28b0aa76a15e0da70a08a5863a2f060e2468" + integrity sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + unrs-resolver@^1.7.11: version "1.12.2" resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.12.2.tgz#a6c6888396abba5adaac4cab6587df866f1d7afd" @@ -2973,6 +4071,68 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +vfile-message@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4" + integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + +vite@^5.4.14: + version "5.4.21" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027" + integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vitepress@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.6.4.tgz#1b6c68fede541a3f401a66263dce0c985e2d8d92" + integrity sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg== + dependencies: + "@docsearch/css" "3.8.2" + "@docsearch/js" "3.8.2" + "@iconify-json/simple-icons" "^1.2.21" + "@shikijs/core" "^2.1.0" + "@shikijs/transformers" "^2.1.0" + "@shikijs/types" "^2.1.0" + "@types/markdown-it" "^14.1.2" + "@vitejs/plugin-vue" "^5.2.1" + "@vue/devtools-api" "^7.7.0" + "@vue/shared" "^3.5.13" + "@vueuse/core" "^12.4.0" + "@vueuse/integrations" "^12.4.0" + focus-trap "^7.6.4" + mark.js "8.11.1" + minisearch "^7.1.1" + shiki "^2.1.0" + vite "^5.4.14" + vue "^3.5.13" + +vue@^3.5.13, vue@^3.5.22: + version "3.5.38" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.38.tgz#8a6d52f1768e197545e937920d6c197fe76fc2db" + integrity sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A== + dependencies: + "@vue/compiler-dom" "3.5.38" + "@vue/compiler-sfc" "3.5.38" + "@vue/runtime-dom" "3.5.38" + "@vue/server-renderer" "3.5.38" + "@vue/shared" "3.5.38" + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -3069,3 +4229,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==