diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7378a2d..f3473da 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,16 +1,19 @@ # Copilot Instructions ## Project Overview -This repository contains `@blwatkins/utils`, a growing ESM-first TypeScript utility package for reusable number, random, and string helpers. + +This repository contains `@blwatkins/utils`, a growing ESM-first TypeScript utility package for reusable number, random, string, and discriminator type-guard helpers. The package source is maintained in `src/`, bundled to `_dist/` with `tsdown`, and documented through TypeDoc output plus manually maintained GitHub Pages release docs under `docs/`. ## Companion Instruction Files + This repository maintains a companion `CLAUDE.md` at the repository root alongside this file. The two documents serve overlapping audiences and should stay consistent: when you update guidance in `.github/copilot-instructions.md` that also applies to `CLAUDE.md`, mirror the change there, and vice versa. `CLAUDE.md` is intentionally a concise pointer to this file; this file remains the canonical, detailed source of conventions. ## Tech Stack + - TypeScript source compiled and bundled for ESM distribution - Node.js runtime support aligned with `package.json` engines (`^22.22.0 || >=24`) - `tsdown` for package builds and declaration output @@ -20,19 +23,102 @@ The two documents serve overlapping audiences and should stay consistent: when y - Jekyll-based `docs/` site content for published project and release documentation ## Development and Validation + Primary development work happens in `src/` and corresponding tests under `test/`. Shared test fixtures and scenario helpers live under `test/utils`. +Vitest also type-checks test files at run time (in addition to executing them), configured via the `typecheck` block in `vitest.config.ts` against `tsconfig.vitest.json`. ### Development Status + The package is currently in an alpha release line and exports grouped utility modules from `src/discriminator`, `src/number`, `src/random`, and `src/string`. ### Validation Steps + - Install dependencies with `npm ci`. - Run lint checks with `npm run lint:all`. - Build with `npm run build`. - Run tests with `npm run test`. +## Pre-Merge and Release Review + +Complete the following steps before merging a branch to a release branch or to `main`. + +### 1. Validation + +Run the full validation suite and confirm everything passes cleanly: + +- `npm ci` — install dependencies from the lockfile +- `npm run lint:all` — both ESLint configurations report no errors +- `npm run build` — build completes with no errors +- `npm test` — all Vitest tests pass + +### 2. Portfolio Skills Page (`docs/portfolio-skills.md`) + +Review `docs/portfolio-skills.md` against the current repository state. If anything changed: + +- Update any section where capabilities, tooling, or the skills inventory changed +- Bump `modified_date` to today; do not change the original `date` +- Evidence links must always point to the `main` branch + +Refer to the "Portfolio Page Generation and Maintenance" section for the full review checklist. + +### 3. Instruction File Sync + +Verify that `CLAUDE.md` and `.github/copilot-instructions.md` are consistent with each other and reflect the current project state: + +- Guidance shared between the two files is mirrored +- The Development Status section accurately lists all modules exported by the package +- Any new tooling, conventions, or workflows introduced on the branch are documented + +### 4. `package.json` Keywords + +Review the `keywords` array in `package.json`: + +- Keywords should cover all major utility domains and notable features exported by the package +- Add new keywords when a new utility domain or notable feature is introduced +- Remove keywords for capabilities that no longer exist + +### 5. GitHub Repository Topics + +Verify that the topics on the GitHub repository ([blwatkins/typescript-utils](https://github.com/blwatkins/typescript-utils)) reflect the current capabilities. Topics should align with `package.json` keywords where appropriate. Request the current topics to be updated, if necessary. Provide any topic change suggestions to the project maintainers and any accepted changes will be updated manually. + +### 6. Branch Changes Code Review + +Review all source changes for convention compliance and code quality. + +#### Convention Compliance + +- New source files follow the static class pattern (private constructor, `@throws` on constructor, public static members only) +- All public/exported members have complete JSDoc per the documentation comment conventions in this file +- Copyright year headers are present and accurate (see "File Headers" section) +- `README.md` and `docs/index.md` are in sync for any shared content changes +- Test coverage is complete and meaningful for all new or changed public API surface + +#### Code Quality + +- **Correctness** — implementations behave exactly as documented; edge cases are handled; patterns (e.g., regex) match precisely what they claim to match +- **API consistency** — new methods and classes follow the naming conventions and structural patterns of existing ones; the public surface is intuitive alongside what is already exported +- **Efficiency** — utility functions avoid unnecessary computation (e.g., no redundant regex compilation, no unnecessary copies or iterations) +- **Backward compatibility** — no unintentional breaking changes to the published API; any intentional breaking changes are reflected in the version bump +- **Reuse and DRY** — new utilities delegate to existing ones where appropriate rather than duplicating logic +- **Runtime safety** — see the "JavaScript Consumer Safety" section for the requirement to retain runtime type guards for JavaScript consumers + +#### Consistency and Pattern Observation + +- **Cross-source consistency** — Compare all changed code, inline comments, and documentation (JSDoc, README, `docs/`) against each other and against implicit patterns visible in the rest of the codebase. Flag any deviation from an established pattern even if that pattern has not been explicitly documented in this file (e.g., consistent phrasing in JSDoc summaries, a structural idiom repeated across utility classes, a naming convention used throughout tests). +- **Implicit pattern detection** — When a consistent pattern is observed in the codebase that is not yet captured in this file, call it out explicitly and ask the maintainer whether it should be documented in the appropriate section of `.github/copilot-instructions.md`. + +### 7. Release Readiness (for merges to `main`) + +When preparing a release merge to `main`: + +- Confirm the version in `package.json` is bumped appropriately +- Ensure release documentation under `docs/releases/` covers the new version +- Verify `typedoc.json` entry points include any new module-level index files +- Confirm the npm publish workflow (`package-publish.yml`) is configured correctly for the release + ## npm Scripts + - `npm run lint:js` - lint repository files with `eslint.config.js.mjs` - `npm run lint:ts` - lint repository files with `eslint.config.ts.mjs` - `npm run lint:all` - run both lint configurations @@ -45,34 +131,41 @@ The package is currently in an alpha release line and exports grouped utility mo - `npm run prepack` - build the package before packing or publishing ## GitHub Actions CI + | Workflow file | Name | Trigger | Description | |---|---|---|---| -| `codeql.yml` | CodeQL | Push/PR to `main`, monthly schedule | Runs CodeQL security analysis for `actions`, `javascript-typescript`, and `ruby` | +| `codeql.yml` | CodeQL | Push/PR to `main` and `release/**`, manual, monthly schedule | Runs CodeQL security analysis for `actions`, `javascript-typescript`, and `ruby` | | `gh-pages-jekyll.yml` | Deploy GitHub Pages with Jekyll | Push to `main`, manual | Builds and deploys the `docs/` directory to GitHub Pages | | `package-publish.yml` | npm and GitHub Package Publish | Manual (`workflow_dispatch`) | Lints, builds, tests, then publishes to npm and GitHub Packages; requires `release_tag` input and uses `id-token: write` trusted publishing permissions for the npm publish job | -| `npm-test.yml` | npm Lint, Build, and Test | Push/PR to `main`, manual | Runs lint, build, and tests across supported Node.js versions | +| `npm-test.yml` | npm Lint, Build, and Test | Push/PR to `main` and `release/**`, manual | Runs lint, build, and tests across supported Node.js versions | ## Security and Dependency Management + - GitHub Actions runs CodeQL analysis for `actions`, `javascript-typescript`, and `ruby`. - Dependabot is configured for monthly updates to npm dependencies, GitHub Actions workflows, and Bundler dependencies under `docs/`. - The npm publish workflow uses npm Trusted Publishing with GitHub OIDC (`id-token: write`) instead of a committed registry token. - The package declares `typebox` as a runtime dependency in `package.json`; all other toolchain packages are maintained through `devDependencies`. ## Development Guidelines + Keep changes scoped to existing files unless a task explicitly requires scaffolding project code. ### TypeScript Conventions + - The package is ESM-only (`"type": "module"`), so keep imports/exports compatible with Node.js ESM resolution. - Public exports flow through module index files. For example, `src/index.ts` re-exports from `src/number/index.ts`, which re-exports from individual utility files. This pattern is intentional to maintain clear module boundaries and organization in both source code and generated documentation. - API documentation entry points stay module-scoped rather than pointing TypeDoc at the root package entry point. #### tsdown Build Output + This project uses `tsdown` to bundle and emit declaration files. When the output format is `esm`, tsdown emits format-specific file extensions: `.mjs` for the bundle and `.d.mts` for the declaration file, regardless of whether the source files use the `.ts` or `.mts` extension. The `types`, `module`, `main`, and `exports` fields in `package.json` should always reference these `.mjs`/`.d.mts` paths (e.g., `./_dist/index.mjs` and `./_dist/index.d.mts`). #### JavaScript Consumer Safety + This package is published as ESM and targets both TypeScript and JavaScript consumers. Retain runtime type guards and input validation even when TypeScript's type system would catch the same issue at compile time. JavaScript callers have no compile-time safety, so runtime checks are necessary for correctness. #### Static Classes + Static utility classes must: - Have a `private constructor()` that throws an `Error` to prevent instantiation - Include a JSDoc `@throws` on the constructor documenting the instantiation error @@ -81,17 +174,23 @@ Static utility classes must: ## Code Style #### Code Style Preferences and Conventions + - Prefer `if`/`else` blocks over ternary operators for conditional logic. - Prefer `@returns` (not `@return`) in TSDoc comments. +- Module-level private constants (e.g., lookup tables backing a set of public getters) use camelCase naming. +- Variable and constant names do not need to encode their type or role in a suffix (e.g., `Pattern`) unless doing so is necessary to clarify the data they hold; surrounding context is often sufficient (e.g., `regularExpressions.hexColor` versus the public `hexColorPattern` getter that exposes it). #### Formatting Rules + - Keep formatting compatible with the repository ESLint configurations in `eslint.config.js.mjs` and `eslint.config.ts.mjs`. - Do not introduce formatting-only tooling or workflow changes unless the task explicitly requires them. ### Documentation Comment Preferences + When writing or reviewing code, follow these documentation standards for maximum compatibility: - **Use `@returns` instead of `@return`**: Always use `@returns` in documentation comments for compatibility with documentation generators. +- **Use `{@link ...}` syntax in `@see` tags**: Always use `{@link ClassName.method}` (or `{@link symbol}`) inside `@see` tags. Do not use bare `{ClassName.method}` without `@link`. - **Use `@param {type} name` format**: Always specify parameter types with the format `@param {type} name` (e.g., `@param {string} hex`) rather than `@param name {type}`. - **Always specify return types with `@returns`**: Include a type indicator in every `@returns` annotation (e.g., `@returns {boolean}`). - **Document void returns with `@returns {void}`**: For methods that do not return a value, explicitly use `@returns {void}`. @@ -132,6 +231,7 @@ Place annotations in the following order for consistency and readability: Include other relevant tags (such as `@template`, `@type`) after the above, as appropriate for the context. ### File Headers + All source files must include the MIT License copyright header at the top. **Copyright year convention:** Use the original year the file was authored. If the file is subsequently modified in a later year, expand to a range (e.g., `2024-2026`). Do not change the starting year when editing an existing file. @@ -159,6 +259,7 @@ All source files must include the MIT License copyright header at the top. ``` ## Directory Structure + - `src/` - package source code - `test/` - Vitest test suites - `test/utils` - Shared test fixtures and scenario helpers for use across test suites @@ -168,31 +269,38 @@ All source files must include the MIT License copyright header at the top. - `.github/workflows/` - CI, publishing, documentation, and analysis workflows ## Documentation and GitHub Pages + - `README.md` and `docs/index.md` should stay in sync for shared content, but they are not expected to be identical. Expected differences include Jekyll front matter, file-specific introductory or heading sections, footer or copyright text, and internal link differences. Any addition, removal, or update to shared sections must be applied consistently to both files. ### TypeDoc Configuration + - API docs are generated with TypeDoc (`npm run docs`) using `typedoc.json`. - TypeDoc entry points are intentionally pointed to module-level index files (e.g., `./src/number/index.ts`, `./src/random/index.ts`) rather than the root package entry point (e.g., `./src/index.ts`). This is done purposefully to maintain module-level organization in the generated documentation output. Do not change TypeDoc entry points to the root package entry point. ### Release Documentation + - Release documentation organized in `docs/releases/...` is maintained manually and not generated by any automated process. - Release docs follow the directory structure: `docs/releases/v{major}.x/v{major}.{minor}.x/v{version-prefix}.x/{full-version}/doc/`, where `{version-prefix}` includes the major, minor, patch, and any pre-release type identifier (e.g., `v0.1.0-alpha` for versions like `v0.1.0-alpha.0`). ### Jekyll Build + - The Jekyll build uses the `jekyll-relative-links` plugin (configured in `docs/_config.yml`), which automatically converts relative `.md` links in `docs/` markdown files to their rendered `.html` paths. For example, `./portfolio-skills.md` in `docs/index.md` resolves to `portfolio-skills.html` on the published site. Use `.md` relative links within `docs/` source files; the build process will convert them correctly. ## Portfolio Page Generation and Maintenance + - The portfolio skills page for this repository lives at `docs/portfolio-skills.md` and is published through the Jekyll site under `docs/`. - Evidence links in `docs/portfolio-skills.md` should always point to the `main` branch, even when the page is updated from another branch. - The guidance in this section applies only when `docs/portfolio-skills.md` is present or intentionally being created. ### Prompt Template + Use the following prompt template when generating or updating the `docs/portfolio-skills.md` page; for example, when a new project is started, when key dependencies or tooling change, or when the project's capabilities, functionality, or implementation evolve. ````markdown You are generating/updating a technical portfolio page documenting a software project, template, starter, or implementation, following a specific evidence-based structure. ## Context + Project Name: [PROJECT_NAME] Project Repository: [GITHUB_REPO_URL] Target Ref for Evidence Links: main @@ -202,6 +310,7 @@ Runtime: [e.g., Node.js, Python 3.11+] Key Technologies: [list 3-5 core tech choices] ## Structure Requirements + Generate a Markdown file with these sections in order: 1. **Front Matter** (Jekyll metadata): @@ -234,10 +343,11 @@ Generate a Markdown file with these sections in order: Use consistent capitalization across similar projects, keep labels semantically precise, and keep tool mentions durable (avoid hardcoded versions, exact cadences, or similarly brittle operational details unless they are intentionally maintained). -5. **Skills and Tooling Inventory** (categorized lists) +5. **Skills and Tooling Inventory** (flat bulleted list with bold category labels, e.g., `- **Category:** [Tool](url), [Tool](url)`) - Languages - - Runtime & Frameworks (or similar category) - - Key libraries and middleware + - Runtime (or similar category) + - Frameworks (or similar category) + - Libraries (or similar category) - Testing - Build / Bundling - Code Quality @@ -250,12 +360,13 @@ Generate a Markdown file with these sections in order: - Code Analysis / Security - Dependency Automation - Development Utilities - - Environment Management + - Environment Configuration (or similar category) - Development Environments - AI-Assisted Development Link each tool/language to its official documentation. Keep categories semantically precise: do not group unrelated concerns together (for example, CI automation, deployment/hosting, code analysis/security, and dependency automation should usually remain separate). + Omit categories that do not apply to the project; add context-specific categories where appropriate. 6. **Capability Record** (bulleted list) - 5–10 bullets describing what this project demonstrates @@ -285,6 +396,7 @@ Generate a Markdown file with these sections in order: - Avoid defensive language; treat gaps as engineering decisions or next-step opportunities ## Tone & Style Guidelines + - **Clarity:** Use precise technical language; avoid marketing speak - **Evidence-first:** Every statement in "Detailed Technical Notes" must be traceable - **Durability:** Generalize version/cadence claims unless you will actively maintain them @@ -296,6 +408,7 @@ Generate a Markdown file with these sections in order: - **Scope discipline:** Do not overstate maturity, completeness, or production-readiness unless directly supported by evidence ## Common Pitfalls to Avoid + - Claim/evidence mismatch (e.g., claiming workflow setup but only linking asset files) - Evidence that is technically relevant but not representative of the current runtime/configured implementation - Overstated scope (e.g., "full-stack" when really just frontend or backend) @@ -306,6 +419,7 @@ Generate a Markdown file with these sections in order: - Capability bullets that list technologies without explaining engineering value ## Output Format + Return the complete Markdown file ready to save as `docs/portfolio-skills.md` and publish. Ensure: - All links use the full GitHub repo URL with `/blob/main/` path format for files - Use `/tree/main/` for directory links when appropriate @@ -315,6 +429,7 @@ Return the complete Markdown file ready to save as `docs/portfolio-skills.md` an - The page reads as evidence-based, concise, and professional ## Update Mode (when `docs/portfolio-skills.md` already exists) + - Preserve any accurate, still-relevant sections and links that do not need changes - Update only sections where repository evidence, tooling, or capabilities changed - Keep front matter `date` from the original file; only update `modified_date` @@ -322,6 +437,7 @@ Return the complete Markdown file ready to save as `docs/portfolio-skills.md` an ```` ### How to Use This Template + 1. **Customize the bracketed fields** at the top with your project's info: - `[PROJECT_NAME]` → actual name - `[GITHUB_REPO_URL]` → full URL @@ -351,6 +467,7 @@ Return the complete Markdown file ready to save as `docs/portfolio-skills.md` an - Ask Copilot to add stronger evidence links where claims are currently under-supported ### Example Customization + If you were documenting a new project called `my-ml-starter`: ```text @@ -365,9 +482,11 @@ Key Technologies: PyTorch, pre-trained models, Docker, GitHub Actions Then paste the full template prompt with these values filled in. ## Portfolio Skills Page Review Instructions + Use the following standards for Copilot code review and any agentic Copilot sessions reviewing changes to `docs/portfolio-skills.md`. ### Reusable Summary for This Portfolio Page Pattern + These pages follow a strong, repeatable structure: 1. **Concise project framing** @@ -382,6 +501,7 @@ The core standard is: **every technical claim should be durable and traceable to ### What to Verify When Reviewing `docs/portfolio-skills.md` #### 1) Structure and completeness + Ensure the page includes the required front matter and these sections (or equivalents): - Required Front Matter (`title`, `layout`, `date`, `modified_date`) @@ -395,6 +515,7 @@ Ensure the page includes the required front matter and these sections (or equiva Why: this keeps pages consistent and easy to compare across projects. #### 2) Claim quality (accuracy + durability) + Check that claims are: - **specific enough to be meaningful** @@ -408,6 +529,7 @@ Risky pattern: - hardcoding exact versions/cadences unless you plan frequent updates #### 3) Evidence alignment (most important review item) + For each claim in technical notes, verify linked evidence **directly supports** it. Evidence does not need to enumerate every implementation instance in the repository. A representative selection that successfully demonstrates the claim is sufficient. Example rule: @@ -418,6 +540,7 @@ Also check whether the linked evidence is **representative of the project's curr This is a common high-impact review issue. #### 4) Portfolio tone calibration + Look for balance between: - implementation facts ("what exists") @@ -429,10 +552,12 @@ Avoid: - absolute claims not backed by links #### 5) Consistency across pages + When reviewing a new page, compare with existing template pages for: - heading style/casing - label style in `At a Glance` +- label style and format in `Skills and Tooling Inventory` (flat bulleted list with bold category labels) - tense and sentence style - bullet punctuation consistency - naming conventions (`webpack` vs `Webpack`, etc.) @@ -440,6 +565,7 @@ When reviewing a new page, compare with existing template pages for: Consistency boosts professionalism at portfolio scale. #### 6) Gaps section quality + A strong `Current Gaps / Future Improvements` section is: - concise (2–4 bullets) @@ -453,6 +579,7 @@ Common high-value bullets: - deployment/docs not yet covered (if true) ### Quick Review Checklist + Reuse the earlier template usage checklist as the canonical baseline review list. Use this section only for additional review-specific checks: ```markdown @@ -463,6 +590,7 @@ Reuse the earlier template usage checklist as the canonical baseline review list ``` ### Common Pitfalls to Catch Early + - Claim/evidence mismatch (most frequent) - Hardcoded version/cadence details that will drift - "CI/CD" wording when no deployment pipeline is shown @@ -472,6 +600,7 @@ Reuse the earlier template usage checklist as the canonical baseline review list - Mixed category labels in tooling inventory that blur automation, deployment, security, and dependency management ### One-Sentence Review Standard + When you review the next page, use this rule: **"If a reader challenges any technical statement, can I point to an exact linked file that proves it, and is the wording likely to stay accurate over time?"** diff --git a/.gitignore b/.gitignore index c609ed4..4a34af8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ # Visual Studio Code .vscode/ +# Claude Code +.claude/ + # Node.js and npm node_modules/ diff --git a/CLAUDE.md b/CLAUDE.md index 53b93c9..4fa8282 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,17 +3,21 @@ Guidance for Claude Code (and other AI assistants) when working in this repository. ## Canonical Instructions + The detailed, authoritative conventions for this project live in [`.github/copilot-instructions.md`](./.github/copilot-instructions.md); read that file first. This document is a concise map; `.github/copilot-instructions.md` is the source of truth. ## Keep These Two Files in Sync + This repository maintains both `CLAUDE.md` and `.github/copilot-instructions.md`. When you update guidance in one file that also applies to the other, mirror the change so the two stay consistent. Updates to `CLAUDE.md` should be reflected, when appropriate, in `.github/copilot-instructions.md`, and vice versa. ## Project Summary -A growing ESM-first TypeScript utility package for reusable number, random, and string helpers. + +A growing ESM-first TypeScript utility package for reusable number, random, string, and discriminator type-guard helpers. ## Common Commands + - `npm ci` — install dependencies from the lockfile. - `npm run lint:all` — run both the JavaScript and TypeScript ESLint configurations. - `npm run build` — bundle the package to `_dist/` with tsdown. @@ -22,13 +26,27 @@ A growing ESM-first TypeScript utility package for reusable number, random, and - `npm run docs` — generate TypeDoc API documentation to `_doc/`. ## Generated Output Directories (not committed) -`_dist/` (build), `_coverage/` (coverage), and `_doc/` (TypeDoc) are generated and gitignored. + +`_dist/` (build), `_compiled/` (TypeScript `outDir`), `_coverage/` (coverage), and `_doc/` (TypeDoc) are generated and gitignored. Note that the `docs/` Jekyll site separately includes committed sample documentation. ## Documentation Notes + - Keep shared content between `README.md` and `docs/index.md` consistent; expected differences (front matter, headings, footer/links) are documented in `.github/copilot-instructions.md`. - The portfolio skills page (`docs/portfolio-skills.md`) and its generation/review workflow are described in the "Portfolio Page Generation and Maintenance" section of `.github/copilot-instructions.md`. - Release documentation under `docs/releases/` is maintained manually. + +## Pre-Merge and Release Review + +Before merging a branch, complete these review steps (full details in the "Pre-Merge and Release Review" section of [`.github/copilot-instructions.md`](./.github/copilot-instructions.md)): + +1. **Validation** — `npm ci`, `npm run lint:all`, `npm run build`, `npm test` all pass +2. **Portfolio skills page** — review `docs/portfolio-skills.md` for accuracy; update `modified_date` if content changes +3. **Instruction file sync** — `CLAUDE.md` and `copilot-instructions.md` are consistent and current +4. **`package.json` keywords** — reflect current utility domains and features +5. **GitHub repository topics** — align with `package.json` keywords +6. **Branch code review** — convention compliance (static class pattern, JSDoc completeness, copyright headers, README/docs sync, test coverage), code quality (correctness, API consistency, efficiency, backward compatibility, reuse/DRY, runtime safety), and consistency (cross-source consistency across code/comments/docs, implicit pattern detection with maintainer notification) +7. **Release readiness** (for merges to `main`) — version bump, release docs, TypeDoc entry points, publish workflow diff --git a/LICENSE b/LICENSE index 2ea7cf5..15d9695 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024-2026 Brittni Watkins +Copyright (c) 2022-2026 Brittni Watkins Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f00d21c..9c0ae98 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,11 @@ A growing toolkit of reusable, domain-agnostic TypeScript and JavaScript utiliti - [Latest Release](https://blwatkins.github.io/typescript-utils/doc/index.html) - [Documentation by Version Number](https://blwatkins.github.io/typescript-utils/releases.html) +## History and Origins + +Development for this project originally began in March 2022 when I started creating utility classes in JavaScript to use across my personal algorithmic generative art projects. +Over the years, the utilities have been refined, expanded, updated, and organized into a series of cohesive libraries that can be used in a variety of projects. + ## License The source code of this project is licensed under the [MIT License](https://opensource.org/license/mit). @@ -71,9 +76,9 @@ The full text of the license is included with the project source code. A huge thank you to all the open source contributors who have made this project possible by creating and maintaining the libraries and tools used in this project, and to the open source community for fostering collaboration and innovation. -A special thank you to all the educators, mentors, and content creators who have shared their knowledge and expertise in the fields of web development and computer science. +A special thank you to all the educators, mentors, and content creators who have shared their knowledge and expertise in the fields of algorithmic art, web development, and computer science. Thank you for giving me the tools, resources, opportunities, support, and inspiration to learn and grow as a developer. ---- -Copyright © 2024-2026 Brittni Watkins. +Copyright © 2022-2026 Brittni Watkins. diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html index 36ad2ba..df8d047 100644 --- a/docs/_includes/footer.html +++ b/docs/_includes/footer.html @@ -3,7 +3,7 @@
+
diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/number.NumberUtility.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/number.NumberUtility.html index 76982ea..65c6dd5 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/number.NumberUtility.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/number.NumberUtility.html @@ -1,17 +1,17 @@ NumberUtility | @blwatkins/utils - v0.1.0-alpha.0
@blwatkins/utils - v0.1.0-alpha.0
    Preparing search index...

    Class NumberUtility

    Static properties and methods for validating number types.

    0.1.0

    -
    Index
    Index

    Methods

    • Is the given input a finite number?

      Parameters

      • input: unknown

      Returns input is number

      true when the input is a finite number; false otherwise.

      0.1.0

      -
    +
    diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.RandomNumberGeneratorFactory.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.RandomNumberGeneratorFactory.html index d1cf318..09ef6a3 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.RandomNumberGeneratorFactory.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.RandomNumberGeneratorFactory.html @@ -1,6 +1,6 @@ RandomNumberGeneratorFactory | @blwatkins/utils - v0.1.0-alpha.0
    @blwatkins/utils - v0.1.0-alpha.0
      Preparing search index...

      Class RandomNumberGeneratorFactory

      A static factory class for creating a SeededRandomNumberGenerator object.

      0.1.0

      -
      Index

      Methods

      Index

      Methods

      +
      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeedVersions.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeedVersions.html index 3a10ac4..dce9e0e 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeedVersions.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeedVersions.html @@ -1,14 +1,14 @@ SeedVersions | @blwatkins/utils - v0.1.0-alpha.0
      @blwatkins/utils - v0.1.0-alpha.0
        Preparing search index...

        Class SeedVersions

        A static class for accessing different seed versions. Each seed version index is guaranteed to always return the same seed version object, so that the same seed and version will always produce the same sequence of pseudorandom numbers.

        0.1.0

        -
        Index

        Accessors

        Index

        Accessors

        Methods

        Accessors

        Methods

        Methods

        • Parameters

          • index: number

            The index of the seed version to retrieve. Must be a valid SeedVersions index.

          Returns SeedVersion

          • The seed version with the given index.
          • @@ -17,7 +17,7 @@
          • If the index is not a valid seed version index.

          0.1.0

          -
        +
        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeededRandomNumberGenerator.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeededRandomNumberGenerator.html index 3a6db7b..fcb21b7 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeededRandomNumberGenerator.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/random.SeededRandomNumberGenerator.html @@ -1,7 +1,7 @@ SeededRandomNumberGenerator | @blwatkins/utils - v0.1.0-alpha.0
        @blwatkins/utils - v0.1.0-alpha.0
          Preparing search index...

          Class SeededRandomNumberGenerator

          Deterministic seeded pseudorandom number generator. This generator utilizes the xoshiro128** algorithm, which is a pseudorandom number generator suitable for general-purpose use.

          0.1.0

          -
          Index

          Constructors

          Index

          Constructors

          Methods

          Constructors

          • Parameters

            • state: [number, number, number, number]

              Initial 128-bit state. Must be an array with 4 32-bit unsigned integers, where at least one element is greater than 0.

              @@ -9,10 +9,10 @@

              If each element of state is not a 32-bit unsigned integer.

              If state does not have at least one element that is greater than 0.

              0.1.0

              -

          Methods

          • Returns number

              +

          Methods

          • Returns number

            • The next pseudorandom float in the range [0, 1).

            This method advances the internal 128-bit xoshiro128** state by one step. Successive calls produce an independent, uniformly distributed sequence.

            0.1.0

            -
          +
          diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/string.StringUtility.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/string.StringUtility.html index f0228c0..f7d090f 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/string.StringUtility.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/classes/string.StringUtility.html @@ -1,6 +1,6 @@ StringUtility | @blwatkins/utils - v0.1.0-alpha.0
          @blwatkins/utils - v0.1.0-alpha.0
            Preparing search index...

            Class StringUtility

            Static properties and methods for validating string types.

            0.1.0

            -
            Index
            Index

            Accessors

            • get singleLineLowercaseTrimmedPattern(): RegExp

              Returns RegExp

              Regular expression pattern for validating single-line lowercase strings.

              This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.

              0.1.0

              -
            • get singleLineTrimmedPattern(): RegExp

              Returns RegExp

              Regular expression pattern for validating single-line mixed-case strings.

              +

            Methods

            Methods

            • Is the given input a non-empty string? Non-empty strings must contain at least one non-whitespace character.

              Parameters

              • input: unknown

              Returns boolean

              0.1.0

              -
            • Is the given input a single-line lowercase string that is trimmed (no leading or trailing whitespace)?

              +
            • Is the given input a single-line string that is trimmed (no leading or trailing whitespace)?

              +
            • Is the given input a single-line uppercase string that is trimmed (no leading or trailing whitespace)?

              +
            • Is the given input a string?

              Parameters

              • input: unknown

              Returns input is string

              0.1.0

              -
            +
            diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/discriminator.DiscriminatorRegistration.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/discriminator.DiscriminatorRegistration.html index e27de8f..bc8bab8 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/discriminator.DiscriminatorRegistration.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/discriminator.DiscriminatorRegistration.html @@ -1,14 +1,14 @@ DiscriminatorRegistration | @blwatkins/utils - v0.1.0-alpha.0
            @blwatkins/utils - v0.1.0-alpha.0
              Preparing search index...

              Interface DiscriminatorRegistration

              A registration for a discriminator to the DiscriminatorRegistry.

              0.1.0

              -
              interface DiscriminatorRegistration {
                  discriminator: string;
                  validator: (input: unknown) => boolean;
              }
              Index

              Properties

              interface DiscriminatorRegistration {
                  discriminator: string;
                  validator: (input: unknown) => boolean;
              }
              Index

              Properties

              discriminator: string

              The discriminator value that identifies the type of a Discriminated object. This value must be unique across all registered discriminators.

              0.1.0

              -
              validator: (input: unknown) => boolean

              A method that validates whether an input matches the type associated with the discriminator.

              +
              validator: (input: unknown) => boolean

              A method that validates whether an input matches the type associated with the discriminator.

              Type Declaration

                • (input: unknown): boolean
                • Parameters

                  • input: unknown

                    The input to validate.

                  Returns boolean

                  • true if the input matches the type associated with the discriminator, false otherwise.

              0.1.0

              -
              +
              diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/random.SeedVersion.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/random.SeedVersion.html index 1ea9b9c..6ac3bdd 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/random.SeedVersion.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/interfaces/random.SeedVersion.html @@ -1,8 +1,8 @@ SeedVersion | @blwatkins/utils - v0.1.0-alpha.0
              @blwatkins/utils - v0.1.0-alpha.0
                Preparing search index...

                Interface SeedVersion

                A seed version defines a specific set of offsets for the FNV-1a hashing algorithm. A different set of offsets results in a different hash for the same input, which results in a different initial state for the seeded random number generator, which results in a different sequence of pseudorandom numbers.

                0.1.0

                -
                interface SeedVersion {
                    offsets: readonly [number, number, number, number];
                }
                Index

                Properties

                interface SeedVersion {
                    offsets: readonly [number, number, number, number];
                }
                Index

                Properties

                Properties

                offsets: readonly [number, number, number, number]

                A collection of offset values for the FNV-1a hashing algorithm. Each offset value should be a 32-bit unsigned integer.

                0.1.0

                -
                +
                diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.Discriminated.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.Discriminated.html index 54c63cf..f74dfea 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.Discriminated.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.Discriminated.html @@ -1,3 +1,3 @@ Discriminated | @blwatkins/utils - v0.1.0-alpha.0
                @blwatkins/utils - v0.1.0-alpha.0
                  Preparing search index...

                  Type Alias Discriminated

                  Discriminated: Static<typeof discriminatedSchema>

                  Discriminated objects can be type checked using the discriminator registry.

                  0.1.0

                  -
                  +
                  diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.TypeGuard.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.TypeGuard.html index 89a0309..69d3ae5 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.TypeGuard.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/types/discriminator.TypeGuard.html @@ -1,3 +1,3 @@ TypeGuard | @blwatkins/utils - v0.1.0-alpha.0
                  @blwatkins/utils - v0.1.0-alpha.0
                    Preparing search index...

                    Type Alias TypeGuard<T>

                    TypeGuard: (input: unknown) => input is T

                    A type guard function that checks if an input is of a specific Discriminated type.

                    Type Parameters

                    Type Declaration

                      • (input: unknown): input is T
                      • Parameters

                        • input: unknown

                        Returns input is T

                    0.1.0

                    -
                    +
                    diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/variables/discriminator.discriminatedSchema.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/variables/discriminator.discriminatedSchema.html index e193874..6e7c94b 100644 --- a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/variables/discriminator.discriminatedSchema.html +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.0/doc/variables/discriminator.discriminatedSchema.html @@ -1,3 +1,3 @@ discriminatedSchema | @blwatkins/utils - v0.1.0-alpha.0
                    @blwatkins/utils - v0.1.0-alpha.0
                      Preparing search index...

                      Variable discriminatedSchemaConst

                      discriminatedSchema: TObject<{ discriminator: TReadonly<TString> }> = ...

                      TypeBox schema for validating that an object implements the Discriminated type.

                      0.1.0

                      -
                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/.nojekyll b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/.nojekyll new file mode 100644 index 0000000..e2ac661 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/hierarchy.js b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/hierarchy.js new file mode 100644 index 0000000..fb85f0a --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/hierarchy.js @@ -0,0 +1 @@ +window.hierarchyData = "eJyrVirKzy8pVrKKjtVRKkpNy0lNLsnMzytWsqqurQUAmx4Kpg==" \ No newline at end of file diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/highlight.css b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/highlight.css new file mode 100644 index 0000000..2dc2614 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/highlight.css @@ -0,0 +1,22 @@ +:root { + --light-code-background: #FFFFFF; + --dark-code-background: #1E1E1E; +} + +@media (prefers-color-scheme: light) { :root { + --code-background: var(--light-code-background); +} } + +@media (prefers-color-scheme: dark) { :root { + --code-background: var(--dark-code-background); +} } + +:root[data-theme='light'] { + --code-background: var(--light-code-background); +} + +:root[data-theme='dark'] { + --code-background: var(--dark-code-background); +} + +pre, code, math[display='block'] { background: var(--code-background); } diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/icons.js b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/icons.js new file mode 100644 index 0000000..4fbadc5 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/icons.js @@ -0,0 +1,18 @@ +(function() { + addIcons(); + function addIcons() { + if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); + const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); + svg.innerHTML = `MMNEPVFCICPMFPCPTTAAATR`; + svg.style.display = "none"; + if (location.protocol === "file:") updateUseElements(); + } + + function updateUseElements() { + document.querySelectorAll("use").forEach(el => { + if (el.getAttribute("href").includes("#icon-")) { + el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); + } + }); + } +})() \ No newline at end of file diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/icons.svg b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/icons.svg new file mode 100644 index 0000000..be7798f --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/icons.svg @@ -0,0 +1 @@ +MMNEPVFCICPMFPCPTTAAATR \ No newline at end of file diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/main.js b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/main.js new file mode 100644 index 0000000..b0eda9e --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/main.js @@ -0,0 +1,60 @@ +"use strict"; +window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This member is normally hidden due to your filter settings.","hierarchy_expand":"Expand","hierarchy_collapse":"Collapse","folder":"Folder","search_index_not_available":"The search index is not available","search_no_results_found_for_0":"No results found for {0}","kind_1":"Project","kind_2":"Module","kind_4":"Namespace","kind_8":"Enumeration","kind_16":"Enumeration Member","kind_32":"Variable","kind_64":"Function","kind_128":"Class","kind_256":"Interface","kind_512":"Constructor","kind_1024":"Property","kind_2048":"Method","kind_4096":"Call Signature","kind_8192":"Index Signature","kind_16384":"Constructor Signature","kind_32768":"Parameter","kind_65536":"Type Literal","kind_131072":"Type Parameter","kind_262144":"Accessor","kind_524288":"Get Signature","kind_1048576":"Set Signature","kind_2097152":"Type Alias","kind_4194304":"Reference","kind_8388608":"Document"}; +(()=>{var Ke=Object.create;var he=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var Ze=Object.getOwnPropertyNames;var Xe=Object.getPrototypeOf,Ye=Object.prototype.hasOwnProperty;var et=(t,e)=>()=>{try{return e||t((e={exports:{}}).exports,e),e.exports}catch(n){throw e=0,n}};var tt=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ze(e))!Ye.call(t,i)&&i!==n&&he(t,i,{get:()=>e[i],enumerable:!(r=Ge(e,i))||r.enumerable});return t};var nt=(t,e,n)=>(n=t!=null?Ke(Xe(t)):{},tt(e||!t||!t.__esModule?he(n,"default",{value:t,enumerable:!0}):n,t));var ye=et((me,ge)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=(function(e){return function(n){e.console&&console.warn&&console.warn(n)}})(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,l],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(oc?d+=2:a==c&&(n+=r[l+1]*i[d+1],l+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}if(s.str.length==0&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}s.str.length==1&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),f=s.str.charAt(1),p;f in s.node.edges?p=s.node.edges[f]:(p=new t.TokenSet,s.node.edges[f]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),c=0;c1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}if(s.type===t.QueryLexer.TERM)return t.QueryParser.parseTerm;var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},(function(e,n){typeof define=="function"&&define.amd?define(n):typeof me=="object"?ge.exports=n():e.lunr=n()})(this,function(){return t})})()});var M,G={getItem(){return null},setItem(){}},K;try{K=localStorage,M=K}catch{K=G,M=G}var S={getItem:t=>M.getItem(t),setItem:(t,e)=>M.setItem(t,e),disableWritingLocalStorage(){M=G},disable(){localStorage.clear(),M=G},enable(){M=K}};window.TypeDoc||={disableWritingLocalStorage(){S.disableWritingLocalStorage()},disableLocalStorage:()=>{S.disable()},enableLocalStorage:()=>{S.enable()}};window.translations||={copy:"Copy",copied:"Copied!",normally_hidden:"This member is normally hidden due to your filter settings.",hierarchy_expand:"Expand",hierarchy_collapse:"Collapse",search_index_not_available:"The search index is not available",search_no_results_found_for_0:"No results found for {0}",folder:"Folder",kind_1:"Project",kind_2:"Module",kind_4:"Namespace",kind_8:"Enumeration",kind_16:"Enumeration Member",kind_32:"Variable",kind_64:"Function",kind_128:"Class",kind_256:"Interface",kind_512:"Constructor",kind_1024:"Property",kind_2048:"Method",kind_4096:"Call Signature",kind_8192:"Index Signature",kind_16384:"Constructor Signature",kind_32768:"Parameter",kind_65536:"Type Literal",kind_131072:"Type Parameter",kind_262144:"Accessor",kind_524288:"Get Signature",kind_1048576:"Set Signature",kind_2097152:"Type Alias",kind_4194304:"Reference",kind_8388608:"Document"};var pe=[];function X(t,e){pe.push({selector:e,constructor:t})}var Z=class{alwaysVisibleMember=null;constructor(){this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){pe.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!rt(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function rt(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var fe=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var Ie=nt(ye(),1);async function R(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0)),r=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate")),i=await new Response(r).text();return JSON.parse(i)}var Y="closing",ae="tsd-overlay";function it(){let t=Math.abs(window.innerWidth-document.documentElement.clientWidth);document.body.style.overflow="hidden",document.body.style.paddingRight=`${t}px`}function st(){document.body.style.removeProperty("overflow"),document.body.style.removeProperty("padding-right")}function Ee(t,e){t.addEventListener("animationend",()=>{t.classList.contains(Y)&&(t.classList.remove(Y),document.getElementById(ae)?.remove(),t.close(),st())}),t.addEventListener("cancel",n=>{n.preventDefault(),ve(t)}),e?.closeOnClick&&document.addEventListener("click",n=>{t.open&&!t.contains(n.target)&&ve(t)},!0)}function xe(t){if(t.open)return;let e=document.createElement("div");e.id=ae,document.body.appendChild(e),t.showModal(),it()}function ve(t){if(!t.open)return;document.getElementById(ae)?.classList.add(Y),t.classList.add(Y)}var I=class{el;app;constructor(e){this.el=e.el,this.app=e.app}};var be=document.head.appendChild(document.createElement("style"));be.dataset.for="filters";var le={};function Le(t){for(let e of t.split(/\s+/))if(le.hasOwnProperty(e)&&!le[e])return!0;return!1}var ee=class extends I{key;value;constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),be.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } +`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=S.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){S.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),le[`tsd-is-${this.el.name}`]=this.value,this.app.filterChanged(),this.app.updateIndexVisibility()}};var we=0;async function Se(t,e){if(!window.searchData)return;let n=await R(window.searchData);t.data=n,t.index=Ie.Index.load(n.index),e.innerHTML=""}function _e(){let t=document.getElementById("tsd-search-trigger"),e=document.getElementById("tsd-search"),n=document.getElementById("tsd-search-input"),r=document.getElementById("tsd-search-results"),i=document.getElementById("tsd-search-script"),s=document.getElementById("tsd-search-status");if(!(t&&e&&n&&r&&i&&s))throw new Error("Search controls missing");let o={base:document.documentElement.dataset.base};o.base.endsWith("/")||(o.base+="/"),i.addEventListener("error",()=>{let a=window.translations.search_index_not_available;Pe(s,a)}),i.addEventListener("load",()=>{Se(o,s)}),Se(o,s),ot({trigger:t,searchEl:e,results:r,field:n,status:s},o)}function ot(t,e){let{field:n,results:r,searchEl:i,status:s,trigger:o}=t;Ee(i,{closeOnClick:!0});function a(){xe(i),n.setSelectionRange(0,n.value.length)}o.addEventListener("click",a),n.addEventListener("input",fe(()=>{at(r,n,s,e)},200)),n.addEventListener("keydown",l=>{if(r.childElementCount===0||l.ctrlKey||l.metaKey||l.altKey)return;let d=n.getAttribute("aria-activedescendant"),f=d?document.getElementById(d):null;if(f){let p=!1,v=!1;switch(l.key){case"Home":case"End":case"ArrowLeft":case"ArrowRight":v=!0;break;case"ArrowDown":case"ArrowUp":p=l.shiftKey;break}(p||v)&&ke(n)}if(!l.shiftKey)switch(l.key){case"Enter":f?.querySelector("a")?.click();break;case"ArrowUp":Te(r,n,f,-1),l.preventDefault();break;case"ArrowDown":Te(r,n,f,1),l.preventDefault();break}});function c(){ke(n)}n.addEventListener("change",c),n.addEventListener("blur",c),n.addEventListener("click",c),document.body.addEventListener("keydown",l=>{if(l.altKey||l.metaKey||l.shiftKey)return;let d=l.ctrlKey&&l.key==="k",f=!l.ctrlKey&&!ut()&&l.key==="/";(d||f)&&(l.preventDefault(),a())})}function at(t,e,n,r){if(!r.index||!r.data)return;t.innerHTML="",n.innerHTML="",we+=1;let i=e.value.trim(),s;if(i){let a=i.split(" ").map(c=>c.length?`*${c}*`:"").join(" ");s=r.index.search(a).filter(({ref:c})=>{let l=r.data.rows[Number(c)].classes;return!l||!Le(l)})}else s=[];if(s.length===0&&i){let a=window.translations.search_no_results_found_for_0.replace("{0}",` "${te(i)}" `);Pe(n,a);return}for(let a=0;ac.score-a.score);let o=Math.min(10,s.length);for(let a=0;a`,f=Ce(c.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(f+=` (score: ${s[a].score.toFixed(2)})`),c.parent&&(f=` + ${Ce(c.parent,i)}.${f}`);let p=document.createElement("li");p.id=`tsd-search:${we}-${a}`,p.role="option",p.ariaSelected="false",p.classList.value=c.classes??"";let v=document.createElement("a");v.tabIndex=-1,v.href=r.base+c.url,v.innerHTML=d+`${f}`,p.append(v),t.appendChild(p)}}function Te(t,e,n,r){let i;if(r===1?i=n?.nextElementSibling||t.firstElementChild:i=n?.previousElementSibling||t.lastElementChild,i!==n){if(!i||i.role!=="option"){console.error("Option missing");return}i.ariaSelected="true",i.scrollIntoView({behavior:"smooth",block:"nearest"}),e.setAttribute("aria-activedescendant",i.id),n?.setAttribute("aria-selected","false")}}function ke(t){let e=t.getAttribute("aria-activedescendant");(e?document.getElementById(e):null)?.setAttribute("aria-selected","false"),t.setAttribute("aria-activedescendant","")}function Ce(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(te(t.substring(s,o)),`${te(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(te(t.substring(s))),i.join("")}var lt={"&":"&","<":"<",">":">","'":"'",'"':"""};function te(t){return t.replace(/[&<>"'"]/g,e=>lt[e])}function Pe(t,e){t.innerHTML=e?`
                      ${e}
                      `:""}var ct=["button","checkbox","file","hidden","image","radio","range","reset","submit"];function ut(){let t=document.activeElement;return t?t.isContentEditable||t.tagName==="TEXTAREA"||t.tagName==="SEARCH"?!0:t.tagName==="INPUT"&&!ct.includes(t.type):!1}var D="mousedown",Me="mousemove",$="mouseup",ne={x:0,y:0},Qe=!1,ce=!1,dt=!1,F=!1,Oe=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(Oe?"is-mobile":"not-mobile");Oe&&"ontouchstart"in document.documentElement&&(dt=!0,D="touchstart",Me="touchmove",$="touchend");document.addEventListener(D,t=>{ce=!0,F=!1;let e=D=="touchstart"?t.targetTouches[0]:t;ne.y=e.pageY||0,ne.x=e.pageX||0});document.addEventListener(Me,t=>{if(ce&&!F){let e=D=="touchstart"?t.targetTouches[0]:t,n=ne.x-(e.pageX||0),r=ne.y-(e.pageY||0);F=Math.sqrt(n*n+r*r)>10}});document.addEventListener($,()=>{ce=!1});document.addEventListener("click",t=>{Qe&&(t.preventDefault(),t.stopImmediatePropagation(),Qe=!1)});var re=class extends I{active;className;constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener($,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(D,n=>this.onDocumentPointerDown(n)),document.addEventListener($,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){F||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!F&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var ue=new Map,de=class{open;accordions=[];key;constructor(e,n){this.key=e,this.open=n}add(e){this.accordions.push(e),e.open=this.open,e.addEventListener("toggle",()=>{this.toggle(e.open)})}toggle(e){for(let n of this.accordions)n.open=e;S.setItem(this.key,e.toString())}},ie=class extends I{constructor(e){super(e);let n=this.el.querySelector("summary"),r=n.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)});let i=`tsd-accordion-${n.dataset.key??n.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`,s;if(ue.has(i))s=ue.get(i);else{let o=S.getItem(i),a=o?o==="true":this.el.open;s=new de(i,a),ue.set(i,s)}s.add(this.el)}};function He(t){let e=S.getItem("tsd-theme")||"os";t.value=e,Ae(e),t.addEventListener("change",()=>{S.setItem("tsd-theme",t.value),Ae(t.value)})}function Ae(t){document.documentElement.dataset.theme=t}var se;function Ne(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Re),Re())}async function Re(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let e=await R(window.navigationData);se=document.documentElement.dataset.base,se.endsWith("/")||(se+="/"),t.innerHTML="";for(let n of e)Ve(n,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Ve(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',De(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let c=a.appendChild(document.createElement("ul"));c.className="tsd-nested-navigation";for(let l of t.children)Ve(l,c,i)}else De(t,r,t.class)}function De(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));if(r.href=se+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&(r.classList.add("current"),r.ariaCurrent="page"),t.kind){let i=window.translations[`kind_${t.kind}`].replaceAll('"',""");r.innerHTML=``}r.appendChild(Fe(t.text,document.createElement("span")))}else{let r=e.appendChild(document.createElement("span")),i=window.translations.folder.replaceAll('"',""");r.innerHTML=``,r.appendChild(Fe(t.text,document.createElement("span")))}}function Fe(t,e){let n=t.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/);for(let r=0;r{let i=r.target;for(;i.parentElement&&i.parentElement.tagName!="LI";)i=i.parentElement;i.dataset.dropdown&&(i.dataset.dropdown=String(i.dataset.dropdown!=="true"))});let t=new Map,e=new Set;for(let r of document.querySelectorAll(".tsd-full-hierarchy [data-refl]")){let i=r.querySelector("ul");t.has(r.dataset.refl)?e.add(r.dataset.refl):i&&t.set(r.dataset.refl,i)}for(let r of e)n(r);function n(r){let i=t.get(r).cloneNode(!0);i.querySelectorAll("[id]").forEach(s=>{s.removeAttribute("id")}),i.querySelectorAll("[data-dropdown]").forEach(s=>{s.dataset.dropdown="false"});for(let s of document.querySelectorAll(`[data-refl="${r}"]`)){let o=gt(),a=s.querySelector("ul");s.insertBefore(o,a),o.dataset.dropdown=String(!!a),a||s.appendChild(i.cloneNode(!0))}}}function pt(){let t=document.getElementById("tsd-hierarchy-script");t&&(t.addEventListener("load",Be),Be())}async function Be(){let t=document.querySelector(".tsd-panel.tsd-hierarchy:has(h4 a)");if(!t||!window.hierarchyData)return;let e=+t.dataset.refl,n=await R(window.hierarchyData),r=t.querySelector("ul"),i=document.createElement("ul");if(i.classList.add("tsd-hierarchy"),ft(i,n,e),r.querySelectorAll("li").length==i.querySelectorAll("li").length)return;let s=document.createElement("span");s.classList.add("tsd-hierarchy-toggle"),s.textContent=window.translations.hierarchy_expand,t.querySelector("h4 a")?.insertAdjacentElement("afterend",s),s.insertAdjacentText("beforebegin",", "),s.addEventListener("click",()=>{s.textContent===window.translations.hierarchy_expand?(r.insertAdjacentElement("afterend",i),r.remove(),s.textContent=window.translations.hierarchy_collapse):(i.insertAdjacentElement("afterend",r),i.remove(),s.textContent=window.translations.hierarchy_expand)})}function ft(t,e,n){let r=e.roots.filter(i=>mt(e,i,n));for(let i of r)t.appendChild(je(e,i,n))}function je(t,e,n,r=new Set){if(r.has(e))return;r.add(e);let i=t.reflections[e],s=document.createElement("li");if(s.classList.add("tsd-hierarchy-item"),e===n){let o=s.appendChild(document.createElement("span"));o.textContent=i.name,o.classList.add("tsd-hierarchy-target")}else{for(let a of i.uniqueNameParents||[]){let c=t.reflections[a],l=s.appendChild(document.createElement("a"));l.textContent=c.name,l.href=oe+c.url,l.className=c.class+" tsd-signature-type",s.append(document.createTextNode("."))}let o=s.appendChild(document.createElement("a"));o.textContent=t.reflections[e].name,o.href=oe+i.url,o.className=i.class+" tsd-signature-type"}if(i.children){let o=s.appendChild(document.createElement("ul"));o.classList.add("tsd-hierarchy");for(let a of i.children){let c=je(t,a,n,r);c&&o.appendChild(c)}}return r.delete(e),s}function mt(t,e,n){if(e===n)return!0;let r=new Set,i=[t.reflections[e]];for(;i.length;){let s=i.pop();if(!r.has(s)){r.add(s);for(let o of s.children||[]){if(o===n)return!0;i.push(t.reflections[o])}}}return!1}function gt(){let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t.setAttribute("width","20"),t.setAttribute("height","20"),t.innerHTML='',t}X(re,"a[data-toggle]");X(ie,".tsd-accordion");X(ee,".tsd-filter-item input[type=checkbox]");var qe=document.getElementById("tsd-theme");qe&&He(qe);var yt=new Z;Object.defineProperty(window,"app",{value:yt});_e();Ne();$e();"virtualKeyboard"in navigator&&(navigator.virtualKeyboard.overlaysContent=!0);})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/navigation.js b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/navigation.js new file mode 100644 index 0000000..b715950 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/navigation.js @@ -0,0 +1 @@ +window.navigationData = "eJytlElPwzAQhf8K8jliKSpLb6iUCgk4tFAOVQ9uPDQWtlPZDlCh/neUPXacBbXqLZl535t05i1/kYYfjUboOSQRA4U85AeUEQkCjZbFW0KVLymnAutQIg9tsQ7QCPG06cx4fRpozpCHPqkgaDRwC05ExEFiTUPRxLyviqoSCiLiNtKsNR3c7Fd7r1AdM6xU45yGzgw2VGm5K9F+2twGz5tMDxcD08Wj0CA/sP8vI8nXKs3QQqOHn6TX+meGV4an190WTu4Yxb0+D5DSid5t20wAscDnt9cXw4GFnkZYdmgWZW696jALLCle91poIHM/AI5L9lfebPEdTaaTy9hE1YaI+BocB5M+73Up7Qv7kgi9acqodixqxjGqXJtZ9SyxICGve06fH8HzzALkZjPAzMGJXXqWQDrUFEScI6F8wL4OXcdqyLqb2mBzAALE2d3IaunpQi1AqiwUm7Xzojaxd6CbQAOZMOAgdOOKZLLu8gNTrOLVmVv1keoR1ThRm6JVenDw5XpPVGk7oyxkXHJ4Pn2bAzQnVEZ31neEk9KSik390NPnRzj0cchCOU/UGtcvg9VLWw+ll2aH3Cr9/QHQ6SnC" \ No newline at end of file diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/search.js b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/search.js new file mode 100644 index 0000000..c11e695 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/search.js @@ -0,0 +1 @@ +window.searchData = "eJy1nF1v2zoShv+LfCvkmJ+OfbfdPe0WKIqD9pzuhREsHJtxhLXlQJLTZoP89wNKsjSjGdmU7Vy1sDUvXw4fDilaymuU7X7m0Wz+Gv0vSVfRTMZRuti6aBatknyZJdskXRS7LIqjfbaJZtF2t9pvXP4b+vbmsdhuojhabhZ57vJoFkVv8UHxtlH8F4zJG0mX7rddQXwpox9HT4vMpQUx2jYsbNPyf1yyfizc6veN2/qggU2PftbxrokPsNJRgs5kT1K+uXWSF9lLY7Bu5pjFQ8x5SZJj3Xp5XORntzyqggfnpelyj6es/N5l5xsDCld397zYJKtF4c53BxSu4s7Yo2gtimSXNm6TtHDZw2IZZLgMPXMqjqU+UVjOtDLqqp2bxCoxPZbrQbqCXah0LatyPJ0II7lxd6vGcfHydMysW51bQHDjf748uU/7RXa84eaq8xpV7DLlVt+Xj267aFp+XmTJ4p4uV0zMmZ1vbKT77b2jy2T18dH1ES4GX8vL/yqSTVLQRaAWQxcd91276ileSf4xSZPCfcXeTzY3SvKHMrDp9LHWsUC/l89p4dYDbSRNzDUc/LHLkyJ5duc4eapjL3XUrnuLdLXbEqCqj4OB+oZVDj2pVb71iQHDtQ9g0Uqhdcdn1Z9PLnUZKpP9DY6qj6p8rEHgMRu1QO9eIbS3o+bKi1v7sNtt3CINb/S+Cbi47e529mTbp/avA9r+uNktBrT8UF9+cbufh/Q3uU5f+wrCsXaPVoEBbffdupz0EHrP0ueFlpHOJP+4WBY75l4FCfIxQ0sOTMsif0mXH/bJZnV2y6NS477WCEhLT897HN5fZu6avuAgfndu5VZsYJ/ZIyEDh9CIdmlb7tK8yPbLCxoeYY2jqTrW754RTN2v3pl20lodfAVP3dH74bI82aX0Jh2oHq65bFXPk//Te9q+Vkb11Sd73PjvSfvaFfUl4W2vXfHcxFzsIMl/+Bu0z+nK/Qr3kOTlbV1SR53jAg51p+T33Q7UgvzVl1TYsgAFrjtHWq8K2cBlqKfvIU6/JPnlNjeVyBU9Jnk5tZPlFTKa5OtK612zShyfm1li9z2ye5W0vnM+L0/k+2XwcAB5qceDznWdguNMUDm5YzhaXwfWQHTit3t4yF2RD2ho1IaELgA9He0jmnroXHlJh0+eyh5pMvAQllc4cua6d0ONHIKuYaACeaiDJupMC93zVHZaVkeqHS1/xUACwDnqT+yo9yS1bpS9fOimo91uFlmSrslhV/Vx8GHXP3ebXfa9jOnbM9WK9Mrj1mt/vdvlR/erlPxjURQuo7vX482OHt2vpf/qqQk/ZoORCXb27dOHC81l6/v39fePyw0uLnOI1+5/1w6H2kryg7H3cHPGQLaGLh7DXk+DBw+Zumzc0N1ySBW4YgHIk3S9cV+S1H3Z/XTZcpG7P7Nku3WrEyWBSUmltUlStzloFZVWYH0InHqt5ytYfWeHfz09XS2r+4PWlTzjyfB1l/6+fSpevuMl7bTBJE93qfOhzWp4DT/fe8k8w2E/mu/k+RpW39dhl8yLrHbRvK7nM5yd0/5dHFUnYbPX6HA0N4vkjbqZRnH0kLjNyj9xVhmLo+VuW9/lrHbLffnfu/qyH25ZPjk1m1dX/zaO4vk4VuMbqaZ3d/H8EFx+UX5w0Gg/KQNFFM9FrG5vJkKiQEECBQqUUTyXsVI3dqxRoCSBEgWqKJ4rrkVFAhUK1FE811ygJoEaBZoonhsu0JBAgwJtFM8tF2hJoEWBkyieT7jACQmcoMDbvnG8JYG3KHAaxfNbrsUpCZxiADwPU5YAyo7owFPSM2aDGX4wQMJjIXj2KEMCQyQ8GkKywZQjgUESHg/BMigoSwLDJDwiguVQUJ4EBkp4TATLoqBMCQyV8KgIlkdBuRIYLOFxERMOLUHZEhguMe0PpnwJDJj0zAiWTUkJk5gwKXpblpQw2SlRJWEs25KpUpgw6ZmRLNuSEiYxYdIzI1m2JSVMYsKkZ0aybEtKmMSESc+MZNmWlDCJCZOeGcmyLSlhEhMmPTOSZVtSwiQmTHpmJMu2pIRJTJjyzEi23ipKmMKEKc+MZPFUlDCFCVPlKsgSpihhqrMQlishS5hi1kJMmPLMKJYwRQlTmDDlmVEsYYoSpjBhyjOj+BWcEqYwYcozo1jCFCVMYcKUZ0axhClKmMKEKc+MYglTlDCFCdOeGcUSpilhGhOmPTOKJUxTwjQmTHtmFEuYpoRpTJj2zGiWME0J053tVrnfYgnTzI4LE6Y9M5olTFPCNCZMe2Y0S5imhGlMmPbMaH6fSAnTmDB927er1RQwjQHT0769m6Z8acyX8cRofo9K+TKYL+OJ0SzZhvJlMF/GE6NZsg3ly2C+TMkXS7ahfBnMlyn5Ysk2lC/T2dKXe3qWbMPs6jFfxhNjWLIN5ctgvownxrBkG8qXwXwZj4xhyTYUMIMBM54Zw5JtKGEGE2Y9M4YlzFLCLCbMemYMfydECbOYMOuZMSxhlhJmMWHWM2NYwiwlzGLCrGfGsIRZSpjFhFnPjGUJs5Qw27lxLO8cWcIsc++ICbOeGcsSZilhFhNmPTOWJcxSwiwmzHpmLEuYpYRZTNjEM2NZwiaUsPqj8kjk2WWFqx8t8ica8CHE1+i/9ZmJbH4Hfo3kNJq9vr21ZySz1zdwTOK/861RjUmrocbhGuQpiFZSAVtqgC38NADQAxZ1mMXyV4LqyGl/ONhvBc24FTQmUBA8QwiyNwVdlUFKK/zGTasFpERYL5FUXv8K2woKARQHm8MdBRkbV7G31T86bICRcIbeL2sbAeM8OVsVjbQCwzNcMUfZBMkMkoJPGwJH0JINEirflmwVNJgNYfHdn4zBTAC9MoFumN94gaAEgmGDyP4mCxRBwsxtkGL3nSMwiCB5Imzm9z9HBwb1FoxJGB09z7sBTVAPdNjshT8jgwSCPpuw2dr5+RdomVbLhhWp7q+2QAysEzY0a83rEmBQgSkRBjH9Aa2Vs2BO2FA58k4XsAe6KcKmRMivZ8AwmHN2eAP9umDm2dCZd/oHKtAAINOGksnIgPGfhEIJn8IGkw44UqGpPFIYNFg3ddgK1H1+E4gBMHVY6a/eLwD9A35UmB+miALexBARZismAGMirE/Ns5MgM3BNDBu2w1t9wArYfoh6dyOm1b8yrDx13toD2qCYy7Bi3nkLD+w5wRjKsPSj9+qAEgBKhiUfvCsHdAAQMmxV7bz7BrQAEDKs+va8HQqyD5ZnGVYgWM2Hw1tswC6oPTKsRva8cgc0Ac0ykObm71aA5RUs+0Eiefmi0clsSpBNFTYvvDKzF9ZgrHXYWAMlVAEUQFmFoRzw7BRIJui0DZt0/Q88AVlQF+xQ270PJ4GlEdQKG0hB+YIWyCyY3CpQgizRGvTTBPaz9+4d1GkbVkv9M9Dr6g9NgKoAciPCOG7/AAtIMXAzSKR3mQcJ12EJB38ppNUBxIbtrZ6rB+KBE1hCwurm4ZF2IAISZMLSfKw2gtJYReuwqtvRpKclGuTLhM3wjibDqgIZVMOMEiwA9iYA+7s4ekqeykIRzeZ3b29/A5cwXvI="; \ No newline at end of file diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/style.css b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/style.css new file mode 100644 index 0000000..ec257d5 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/assets/style.css @@ -0,0 +1,1648 @@ +@layer typedoc { + :root { + --dim-toolbar-contents-height: 2.5rem; + --dim-toolbar-border-bottom-width: 1px; + --dim-header-height: calc( + var(--dim-toolbar-border-bottom-width) + + var(--dim-toolbar-contents-height) + ); + + /* 0rem For mobile; unit is required for calculation in `calc` */ + --dim-container-main-margin-y: 0rem; + + --dim-footer-height: 3.5rem; + + --modal-animation-duration: 0.2s; + } + + :root { + /* Light */ + --light-color-background: #f2f4f8; + --light-color-background-secondary: #eff0f1; + /* Not to be confused with [:active](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) */ + --light-color-background-active: #d6d8da; + --light-color-background-warning: #e6e600; + --light-color-warning-text: #222; + --light-color-accent: #c5c7c9; + --light-color-active-menu-item: var(--light-color-background-active); + --light-color-text: #222; + --light-color-contrast-text: #000; + --light-color-text-aside: #5e5e5e; + + --light-color-icon-background: var(--light-color-background); + --light-color-icon-text: var(--light-color-text); + + --light-color-comment-tag-text: var(--light-color-text); + --light-color-comment-tag: var(--light-color-background); + + --light-color-link: #1f70c2; + --light-color-focus-outline: #3584e4; + + --light-color-ts-keyword: #056bd6; + --light-color-ts-project: #b111c9; + --light-color-ts-module: var(--light-color-ts-project); + --light-color-ts-namespace: var(--light-color-ts-project); + --light-color-ts-enum: #7e6f15; + --light-color-ts-enum-member: var(--light-color-ts-enum); + --light-color-ts-variable: #4760ec; + --light-color-ts-function: #572be7; + --light-color-ts-class: #1f70c2; + --light-color-ts-interface: #108024; + --light-color-ts-constructor: var(--light-color-ts-class); + --light-color-ts-property: #9f5f30; + --light-color-ts-method: #be3989; + --light-color-ts-reference: #ff4d82; + --light-color-ts-call-signature: var(--light-color-ts-method); + --light-color-ts-index-signature: var(--light-color-ts-property); + --light-color-ts-constructor-signature: var( + --light-color-ts-constructor + ); + --light-color-ts-parameter: var(--light-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --light-color-ts-type-parameter: #a55c0e; + --light-color-ts-accessor: #c73c3c; + --light-color-ts-get-signature: var(--light-color-ts-accessor); + --light-color-ts-set-signature: var(--light-color-ts-accessor); + --light-color-ts-type-alias: #d51270; + /* reference not included as links will be colored with the kind that it points to */ + --light-color-document: #000000; + + --light-color-alert-note: #0969d9; + --light-color-alert-tip: #1a7f37; + --light-color-alert-important: #8250df; + --light-color-alert-warning: #9a6700; + --light-color-alert-caution: #cf222e; + + --light-external-icon: url("data:image/svg+xml;utf8,"); + --light-color-scheme: light; + } + + :root { + /* Dark */ + --dark-color-background: #2b2e33; + --dark-color-background-secondary: #1e2024; + /* Not to be confused with [:active](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) */ + --dark-color-background-active: #5d5d6a; + --dark-color-background-warning: #bebe00; + --dark-color-warning-text: #222; + --dark-color-accent: #9096a2; + --dark-color-active-menu-item: var(--dark-color-background-active); + --dark-color-text: #f5f5f5; + --dark-color-contrast-text: #ffffff; + --dark-color-text-aside: #dddddd; + + --dark-color-icon-background: var(--dark-color-background-secondary); + --dark-color-icon-text: var(--dark-color-text); + + --dark-color-comment-tag-text: var(--dark-color-text); + --dark-color-comment-tag: var(--dark-color-background); + + --dark-color-link: #00aff4; + --dark-color-focus-outline: #4c97f2; + + --dark-color-ts-keyword: #3399ff; + --dark-color-ts-project: #e358ff; + --dark-color-ts-module: var(--dark-color-ts-project); + --dark-color-ts-namespace: var(--dark-color-ts-project); + --dark-color-ts-enum: #f4d93e; + --dark-color-ts-enum-member: var(--dark-color-ts-enum); + --dark-color-ts-variable: #798dff; + --dark-color-ts-function: #a280ff; + --dark-color-ts-class: #8ac4ff; + --dark-color-ts-interface: #6cff87; + --dark-color-ts-constructor: var(--dark-color-ts-class); + --dark-color-ts-property: #ff984d; + --dark-color-ts-method: #ff4db8; + --dark-color-ts-reference: #ff4d82; + --dark-color-ts-call-signature: var(--dark-color-ts-method); + --dark-color-ts-index-signature: var(--dark-color-ts-property); + --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); + --dark-color-ts-parameter: var(--dark-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --dark-color-ts-type-parameter: #e07d13; + --dark-color-ts-accessor: #ff6060; + --dark-color-ts-get-signature: var(--dark-color-ts-accessor); + --dark-color-ts-set-signature: var(--dark-color-ts-accessor); + --dark-color-ts-type-alias: #ff6492; + /* reference not included as links will be colored with the kind that it points to */ + --dark-color-document: #ffffff; + + --dark-color-alert-note: #0969d9; + --dark-color-alert-tip: #1a7f37; + --dark-color-alert-important: #8250df; + --dark-color-alert-warning: #9a6700; + --dark-color-alert-caution: #cf222e; + + --dark-external-icon: url("data:image/svg+xml;utf8,"); + --dark-color-scheme: dark; + } + + @media (prefers-color-scheme: light) { + :root { + --color-background: var(--light-color-background); + --color-background-secondary: var( + --light-color-background-secondary + ); + --color-background-active: var(--light-color-background-active); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-contrast-text: var(--light-color-contrast-text); + --color-text-aside: var(--light-color-text-aside); + + --color-icon-background: var(--light-color-icon-background); + --color-icon-text: var(--light-color-icon-text); + + --color-comment-tag-text: var(--light-color-text); + --color-comment-tag: var(--light-color-background); + + --color-link: var(--light-color-link); + --color-focus-outline: var(--light-color-focus-outline); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-project: var(--light-color-ts-project); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-reference: var(--light-color-ts-reference); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + --color-document: var(--light-color-document); + + --color-alert-note: var(--light-color-alert-note); + --color-alert-tip: var(--light-color-alert-tip); + --color-alert-important: var(--light-color-alert-important); + --color-alert-warning: var(--light-color-alert-warning); + --color-alert-caution: var(--light-color-alert-caution); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } + } + + @media (prefers-color-scheme: dark) { + :root { + --color-background: var(--dark-color-background); + --color-background-secondary: var( + --dark-color-background-secondary + ); + --color-background-active: var(--dark-color-background-active); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-contrast-text: var(--dark-color-contrast-text); + --color-text-aside: var(--dark-color-text-aside); + + --color-icon-background: var(--dark-color-icon-background); + --color-icon-text: var(--dark-color-icon-text); + + --color-comment-tag-text: var(--dark-color-text); + --color-comment-tag: var(--dark-color-background); + + --color-link: var(--dark-color-link); + --color-focus-outline: var(--dark-color-focus-outline); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-project: var(--dark-color-ts-project); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-reference: var(--dark-color-ts-reference); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + --color-document: var(--dark-color-document); + + --color-alert-note: var(--dark-color-alert-note); + --color-alert-tip: var(--dark-color-alert-tip); + --color-alert-important: var(--dark-color-alert-important); + --color-alert-warning: var(--dark-color-alert-warning); + --color-alert-caution: var(--dark-color-alert-caution); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } + } + + :root[data-theme="light"] { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-active: var(--light-color-background-active); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-contrast-text: var(--light-color-contrast-text); + --color-text-aside: var(--light-color-text-aside); + --color-icon-text: var(--light-color-icon-text); + + --color-comment-tag-text: var(--light-color-text); + --color-comment-tag: var(--light-color-background); + + --color-link: var(--light-color-link); + --color-focus-outline: var(--light-color-focus-outline); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-project: var(--light-color-ts-project); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-reference: var(--light-color-ts-reference); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + --color-document: var(--light-color-document); + + --color-note: var(--light-color-note); + --color-tip: var(--light-color-tip); + --color-important: var(--light-color-important); + --color-warning: var(--light-color-warning); + --color-caution: var(--light-color-caution); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } + + :root[data-theme="dark"] { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-active: var(--dark-color-background-active); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-contrast-text: var(--dark-color-contrast-text); + --color-text-aside: var(--dark-color-text-aside); + --color-icon-text: var(--dark-color-icon-text); + + --color-comment-tag-text: var(--dark-color-text); + --color-comment-tag: var(--dark-color-background); + + --color-link: var(--dark-color-link); + --color-focus-outline: var(--dark-color-focus-outline); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-project: var(--dark-color-ts-project); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-reference: var(--dark-color-ts-reference); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + --color-document: var(--dark-color-document); + + --color-note: var(--dark-color-note); + --color-tip: var(--dark-color-tip); + --color-important: var(--dark-color-important); + --color-warning: var(--dark-color-warning); + --color-caution: var(--dark-color-caution); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } + + html { + color-scheme: var(--color-scheme); + @media (prefers-reduced-motion: no-preference) { + scroll-behavior: smooth; + } + } + + *:focus-visible, + .tsd-accordion-summary:focus-visible svg { + outline: 2px solid var(--color-focus-outline); + } + + .always-visible, + .always-visible .tsd-signatures { + display: inherit !important; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.2; + } + + h1 { + font-size: 1.875rem; + margin: 0.67rem 0; + } + + h2 { + font-size: 1.5rem; + margin: 0.83rem 0; + } + + h3 { + font-size: 1.25rem; + margin: 1rem 0; + } + + h4 { + font-size: 1.05rem; + margin: 1.33rem 0; + } + + h5 { + font-size: 1rem; + margin: 1.5rem 0; + } + + h6 { + font-size: 0.875rem; + margin: 2.33rem 0; + } + + dl, + menu, + ol, + ul { + margin: 1em 0; + } + + dd { + margin: 0 0 0 34px; + } + + .container { + max-width: 1700px; + padding: 0 2rem; + } + + /* Footer */ + footer { + border-top: 1px solid var(--color-accent); + padding-top: 1rem; + padding-bottom: 1rem; + max-height: var(--dim-footer-height); + } + footer > p { + margin: 0 1em; + } + + .container-main { + margin: var(--dim-container-main-margin-y) auto; + /* toolbar, footer, margin */ + min-height: calc( + 100svh - var(--dim-header-height) - var(--dim-footer-height) - + 2 * var(--dim-container-main-margin-y) + ); + } + + @keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + @keyframes fade-out { + from { + opacity: 1; + visibility: visible; + } + to { + opacity: 0; + } + } + @keyframes pop-in-from-right { + from { + transform: translate(100%, 0); + } + to { + transform: translate(0, 0); + } + } + @keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; + } + to { + transform: translate(100%, 0); + } + } + body { + background: var(--color-background); + font-family: + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + "Noto Sans", + Helvetica, + Arial, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji"; + font-size: 16px; + color: var(--color-text); + margin: 0; + } + + a { + color: var(--color-link); + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + a.external[target="_blank"] { + background-image: var(--external-icon); + background-position: top 3px right; + background-repeat: no-repeat; + padding-right: 13px; + } + a.tsd-anchor-link { + color: var(--color-text); + } + :target { + scroll-margin-block: calc(var(--dim-header-height) + 0.5rem); + } + + math[display="block"] { + position: relative; + padding: 10px; + border: 1px solid var(--color-accent); + border-radius: 0.8em; + margin-bottom: 8px; + } + + code, + pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 0.875rem; + border-radius: 0.8em; + } + + pre { + position: relative; + white-space: pre-wrap; + word-wrap: break-word; + padding: 10px; + border: 1px solid var(--color-accent); + margin-bottom: 8px; + } + pre code { + padding: 0; + font-size: 100%; + } + pre > button { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.1s; + box-sizing: border-box; + } + pre:hover > button, + pre > button.visible, + pre > button:focus-visible { + opacity: 1; + } + + blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid gray; + } + + img { + max-width: 100%; + } + + * { + scrollbar-width: thin; + scrollbar-color: var(--color-accent) var(--color-icon-background); + } + + *::-webkit-scrollbar { + width: 0.75rem; + } + + *::-webkit-scrollbar-track { + background: var(--color-icon-background); + } + + *::-webkit-scrollbar-thumb { + background-color: var(--color-accent); + border-radius: 999rem; + border: 0.25rem solid var(--color-icon-background); + } + + dialog { + border: none; + outline: none; + padding: 0; + background-color: var(--color-background); + } + dialog::backdrop { + display: none; + } + #tsd-overlay { + background-color: rgba(0, 0, 0, 0.5); + position: fixed; + z-index: 9999; + top: 0; + left: 0; + right: 0; + bottom: 0; + animation: fade-in var(--modal-animation-duration) forwards; + } + #tsd-overlay.closing { + animation-name: fade-out; + } + + .tsd-typography { + line-height: 1.333em; + } + .tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; + } + .tsd-typography .tsd-index-panel h3, + .tsd-index-panel .tsd-typography h3, + .tsd-typography h4, + .tsd-typography h5, + .tsd-typography h6 { + font-size: 1em; + } + .tsd-typography h5, + .tsd-typography h6 { + font-weight: normal; + } + .tsd-typography p, + .tsd-typography ul, + .tsd-typography ol { + margin: 1em 0; + } + .tsd-typography table { + border-collapse: collapse; + border: none; + } + .tsd-typography td, + .tsd-typography th { + padding: 6px 13px; + border: 1px solid var(--color-accent); + } + .tsd-typography thead, + .tsd-typography tr:nth-child(even) { + background-color: var(--color-background-secondary); + } + + .tsd-alert { + padding: 8px 16px; + margin-bottom: 16px; + border-left: 0.25em solid var(--alert-color); + } + .tsd-alert blockquote > :last-child, + .tsd-alert > :last-child { + margin-bottom: 0; + } + .tsd-alert-title { + color: var(--alert-color); + display: inline-flex; + align-items: center; + } + .tsd-alert-title span { + margin-left: 4px; + } + + .tsd-alert-note { + --alert-color: var(--color-alert-note); + } + .tsd-alert-tip { + --alert-color: var(--color-alert-tip); + } + .tsd-alert-important { + --alert-color: var(--color-alert-important); + } + .tsd-alert-warning { + --alert-color: var(--color-alert-warning); + } + .tsd-alert-caution { + --alert-color: var(--color-alert-caution); + } + + .tsd-breadcrumb { + margin: 0; + margin-top: 1rem; + padding: 0; + color: var(--color-text-aside); + } + .tsd-breadcrumb a { + color: var(--color-text-aside); + text-decoration: none; + } + .tsd-breadcrumb a:hover { + text-decoration: underline; + } + .tsd-breadcrumb li { + display: inline; + } + .tsd-breadcrumb li:after { + content: " / "; + } + + .tsd-comment-tags { + display: flex; + flex-direction: column; + } + dl.tsd-comment-tag-group { + display: flex; + align-items: center; + overflow: hidden; + margin: 0.5em 0; + } + dl.tsd-comment-tag-group dt { + display: flex; + margin-right: 0.5em; + font-size: 0.875em; + font-weight: normal; + } + dl.tsd-comment-tag-group dd { + margin: 0; + } + code.tsd-tag { + padding: 0.25em 0.4em; + border: 0.1em solid var(--color-accent); + margin-right: 0.25em; + font-size: 70%; + } + h1 code.tsd-tag:first-of-type { + margin-left: 0.25em; + } + + dl.tsd-comment-tag-group dd:before, + dl.tsd-comment-tag-group dd:after { + content: " "; + } + dl.tsd-comment-tag-group dd pre, + dl.tsd-comment-tag-group dd:after { + clear: both; + } + dl.tsd-comment-tag-group p { + margin: 0; + } + + .tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; + } + .tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; + } + + .tsd-filter-visibility h4 { + font-size: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.5rem; + margin: 0; + } + .tsd-filter-item:not(:last-child) { + margin-bottom: 0.5rem; + } + .tsd-filter-input { + display: flex; + width: -moz-fit-content; + width: fit-content; + align-items: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + } + .tsd-filter-input input[type="checkbox"] { + cursor: pointer; + position: absolute; + width: 1.5em; + height: 1.5em; + opacity: 0; + } + .tsd-filter-input input[type="checkbox"]:disabled { + pointer-events: none; + } + .tsd-filter-input svg { + cursor: pointer; + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; + border-radius: 0.33em; + /* Leaving this at full opacity breaks event listeners on Firefox. + Don't remove unless you know what you're doing. */ + opacity: 0.99; + } + .tsd-filter-input input[type="checkbox"]:focus-visible + svg { + outline: 2px solid var(--color-focus-outline); + } + .tsd-checkbox-background { + fill: var(--color-accent); + } + input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { + stroke: var(--color-text); + } + .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { + fill: var(--color-background); + stroke: var(--color-accent); + stroke-width: 0.25rem; + } + .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { + stroke: var(--color-accent); + } + + .settings-label { + font-weight: bold; + text-transform: uppercase; + display: inline-block; + } + + .tsd-filter-visibility .settings-label { + margin: 0.75rem 0 0.5rem 0; + } + + .tsd-theme-toggle .settings-label { + margin: 0.75rem 0.75rem 0 0; + } + + .tsd-hierarchy h4 label:hover span { + text-decoration: underline; + } + + .tsd-hierarchy { + list-style: square; + margin: 0; + } + .tsd-hierarchy-target { + font-weight: bold; + } + .tsd-hierarchy-toggle { + color: var(--color-link); + cursor: pointer; + } + + .tsd-full-hierarchy:not(:last-child) { + margin-bottom: 1em; + padding-bottom: 1em; + border-bottom: 1px solid var(--color-accent); + } + .tsd-full-hierarchy, + .tsd-full-hierarchy ul { + list-style: none; + margin: 0; + padding: 0; + } + .tsd-full-hierarchy ul { + padding-left: 1.5rem; + } + .tsd-full-hierarchy a { + padding: 0.25rem 0 !important; + font-size: 1rem; + display: inline-flex; + align-items: center; + color: var(--color-text); + } + .tsd-full-hierarchy svg[data-dropdown] { + cursor: pointer; + } + .tsd-full-hierarchy svg[data-dropdown="false"] { + transform: rotate(-90deg); + } + .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { + display: none; + } + + .tsd-panel-group.tsd-index-group { + margin-bottom: 0; + } + .tsd-index-panel .tsd-index-list { + list-style: none; + line-height: 1.333em; + margin: 0; + padding: 0.25rem 0 0 0; + overflow: hidden; + display: grid; + grid-template-columns: repeat(3, 1fr); + column-gap: 1rem; + grid-template-rows: auto; + } + @media (max-width: 1024px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(2, 1fr); + } + } + @media (max-width: 768px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(1, 1fr); + } + } + .tsd-index-panel .tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; + } + + .tsd-flag { + display: inline-block; + padding: 0.25em 0.4em; + border-radius: 4px; + color: var(--color-comment-tag-text); + background-color: var(--color-comment-tag); + text-indent: 0; + font-size: 75%; + line-height: 1; + font-weight: normal; + } + + .tsd-anchor { + position: relative; + top: -100px; + } + + .tsd-member { + position: relative; + } + .tsd-member .tsd-anchor + h3 { + display: flex; + align-items: center; + margin-top: 0; + margin-bottom: 0; + border-bottom: none; + } + + .tsd-navigation.settings { + margin: 0; + margin-bottom: 1rem; + } + .tsd-navigation > a, + .tsd-navigation .tsd-accordion-summary { + width: calc(100% - 0.25rem); + display: flex; + align-items: center; + } + .tsd-navigation a, + .tsd-navigation summary > span, + .tsd-page-navigation a { + display: flex; + width: calc(100% - 0.25rem); + align-items: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + box-sizing: border-box; + } + .tsd-navigation a.current, + .tsd-page-navigation a.current { + background: var(--color-active-menu-item); + color: var(--color-contrast-text); + } + .tsd-navigation a:hover, + .tsd-page-navigation a:hover { + text-decoration: underline; + } + .tsd-navigation ul, + .tsd-page-navigation ul { + margin-top: 0; + margin-bottom: 0; + padding: 0; + list-style: none; + } + .tsd-navigation li, + .tsd-page-navigation li { + padding: 0; + max-width: 100%; + } + .tsd-navigation .tsd-nav-link { + display: none; + } + .tsd-nested-navigation { + margin-left: 3rem; + } + .tsd-nested-navigation > li > details { + margin-left: -1.5rem; + } + .tsd-small-nested-navigation { + margin-left: 1.5rem; + } + .tsd-small-nested-navigation > li > details { + margin-left: -1.5rem; + } + + .tsd-page-navigation-section > summary { + padding: 0.25rem; + } + .tsd-page-navigation-section > summary > svg { + margin-right: 0.25rem; + } + .tsd-page-navigation-section > div { + margin-left: 30px; + } + .tsd-page-navigation ul { + padding-left: 1.75rem; + } + + #tsd-sidebar-links a { + margin-top: 0; + margin-bottom: 0.5rem; + line-height: 1.25rem; + } + #tsd-sidebar-links a:last-of-type { + margin-bottom: 0; + } + + a.tsd-index-link { + padding: 0.25rem 0 !important; + font-size: 1rem; + line-height: 1.25rem; + display: inline-flex; + align-items: center; + color: var(--color-text); + } + .tsd-accordion-summary { + list-style-type: none; /* hide marker on non-safari */ + outline: none; /* broken on safari, so just hide it */ + display: flex; + align-items: center; + gap: 0.25rem; + box-sizing: border-box; + } + .tsd-accordion-summary::-webkit-details-marker { + display: none; /* hide marker on safari */ + } + .tsd-accordion-summary, + .tsd-accordion-summary a { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + + cursor: pointer; + } + .tsd-accordion-summary > a { + width: calc(100% - 1.5rem); + } + .tsd-accordion-summary > * { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + } + /* + * We need to be careful to target the arrow indicating whether the accordion + * is open, but not any other SVGs included in the details element. + */ + .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child { + transform: rotate(-90deg); + } + .tsd-index-content > :not(:first-child) { + margin-top: 0.75rem; + } + .tsd-index-summary { + margin-top: 1.5rem; + margin-bottom: 0.75rem; + display: flex; + align-content: center; + } + + .tsd-no-select { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + .tsd-kind-icon { + margin-right: 0.5rem; + width: 1.25rem; + height: 1.25rem; + min-width: 1.25rem; + min-height: 1.25rem; + } + .tsd-signature > .tsd-kind-icon { + margin-right: 0.8rem; + } + + .tsd-panel { + margin-bottom: 2.5rem; + } + .tsd-panel.tsd-member { + margin-bottom: 4rem; + } + .tsd-panel:empty { + display: none; + } + .tsd-panel > h1, + .tsd-panel > h2, + .tsd-panel > h3 { + margin: 1.5rem -1.5rem 0.75rem -1.5rem; + padding: 0 1.5rem 0.75rem 1.5rem; + } + .tsd-panel > h1.tsd-before-signature, + .tsd-panel > h2.tsd-before-signature, + .tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: none; + } + + .tsd-panel-group { + margin: 2rem 0; + } + .tsd-panel-group.tsd-index-group { + margin: 2rem 0; + } + .tsd-panel-group.tsd-index-group details { + margin: 2rem 0; + } + .tsd-panel-group > .tsd-accordion-summary { + margin-bottom: 1rem; + } + + #tsd-search[open] { + animation: fade-in var(--modal-animation-duration) ease-out forwards; + } + #tsd-search[open].closing { + animation-name: fade-out; + } + + /* Avoid setting `display` on closed dialog */ + #tsd-search[open] { + display: flex; + flex-direction: column; + padding: 1rem; + width: 32rem; + max-width: 90vw; + max-height: calc(100vh - env(keyboard-inset-height, 0px) - 25vh); + /* Anchor dialog to top */ + margin-top: 10vh; + border-radius: 6px; + will-change: max-height; + } + #tsd-search-input { + box-sizing: border-box; + width: 100%; + padding: 0 0.625rem; /* 10px */ + outline: 0; + border: 2px solid var(--color-accent); + background-color: transparent; + color: var(--color-text); + border-radius: 4px; + height: 2.5rem; + flex: 0 0 auto; + font-size: 0.875rem; + transition: border-color 0.2s, background-color 0.2s; + } + #tsd-search-input:focus-visible { + background-color: var(--color-background-active); + border-color: transparent; + color: var(--color-contrast-text); + } + #tsd-search-input::placeholder { + color: inherit; + opacity: 0.8; + } + #tsd-search-results { + margin: 0; + padding: 0; + list-style: none; + flex: 1 1 auto; + display: flex; + flex-direction: column; + overflow-y: auto; + } + #tsd-search-results:not(:empty) { + margin-top: 0.5rem; + } + #tsd-search-results > li { + background-color: var(--color-background); + line-height: 1.5; + box-sizing: border-box; + border-radius: 4px; + } + #tsd-search-results > li:nth-child(even) { + background-color: var(--color-background-secondary); + } + #tsd-search-results > li:is(:hover, [aria-selected="true"]) { + background-color: var(--color-background-active); + color: var(--color-contrast-text); + } + /* It's important that this takes full size of parent `li`, to capture a click on `li` */ + #tsd-search-results > li > a { + display: flex; + align-items: center; + padding: 0.5rem 0.25rem; + box-sizing: border-box; + width: 100%; + } + #tsd-search-results > li > a > .text { + flex: 1 1 auto; + min-width: 0; + overflow-wrap: anywhere; + } + #tsd-search-results > li > a .parent { + color: var(--color-text-aside); + } + #tsd-search-results > li > a mark { + color: inherit; + background-color: inherit; + font-weight: bold; + } + #tsd-search-status { + flex: 1; + display: grid; + place-content: center; + text-align: center; + overflow-wrap: anywhere; + } + #tsd-search-status:not(:empty) { + min-height: 6rem; + } + + .tsd-signature { + margin: 0 0 1rem 0; + padding: 1rem 0.5rem; + border: 1px solid var(--color-accent); + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; + overflow-x: auto; + } + + .tsd-signature-keyword { + color: var(--color-ts-keyword); + font-weight: normal; + } + + .tsd-signature-symbol { + color: var(--color-text-aside); + font-weight: normal; + } + + .tsd-signature-type { + font-style: italic; + font-weight: normal; + } + + .tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + list-style-type: none; + } + .tsd-signatures .tsd-signature { + margin: 0; + border-color: var(--color-accent); + border-width: 1px 0; + transition: background-color 0.1s; + } + .tsd-signatures .tsd-index-signature:not(:last-child) { + margin-bottom: 1em; + } + .tsd-signatures .tsd-index-signature .tsd-signature { + border-width: 1px; + } + .tsd-description .tsd-signatures .tsd-signature { + border-width: 1px; + } + + ul.tsd-parameter-list, + ul.tsd-type-parameter-list { + list-style: square; + margin: 0; + padding-left: 20px; + } + ul.tsd-parameter-list > li.tsd-parameter-signature, + ul.tsd-type-parameter-list > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; + } + ul.tsd-parameter-list h5, + ul.tsd-type-parameter-list h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; + } + .tsd-sources { + margin-top: 1rem; + font-size: 0.875em; + } + .tsd-sources a { + color: var(--color-text-aside); + text-decoration: underline; + } + .tsd-sources ul { + list-style: none; + padding: 0; + } + + .tsd-page-toolbar { + position: sticky; + z-index: 1; + top: 0; + left: 0; + width: 100%; + color: var(--color-text); + background: var(--color-background-secondary); + border-bottom: var(--dim-toolbar-border-bottom-width) + var(--color-accent) solid; + transition: transform 0.3s ease-in-out; + } + .tsd-page-toolbar a { + color: var(--color-text); + } + .tsd-toolbar-contents { + display: flex; + align-items: center; + height: var(--dim-toolbar-contents-height); + margin: 0 auto; + } + .tsd-toolbar-contents > .title { + font-weight: bold; + margin-right: auto; + } + #tsd-toolbar-links { + display: flex; + align-items: center; + gap: 1.5rem; + margin-right: 1rem; + } + + .tsd-widget { + box-sizing: border-box; + display: inline-block; + opacity: 0.8; + height: 2.5rem; + width: 2.5rem; + transition: opacity 0.1s, background-color 0.1s; + text-align: center; + cursor: pointer; + border: none; + background-color: transparent; + } + .tsd-widget:hover { + opacity: 0.9; + } + .tsd-widget:active { + opacity: 1; + background-color: var(--color-accent); + } + #tsd-toolbar-menu-trigger { + display: none; + } + + .tsd-member-summary-name { + display: inline-flex; + align-items: center; + padding: 0.25rem; + text-decoration: none; + } + + .tsd-anchor-icon { + display: inline-flex; + align-items: center; + margin-left: 0.5rem; + color: var(--color-text); + vertical-align: middle; + } + + .tsd-anchor-icon svg { + width: 1em; + height: 1em; + visibility: hidden; + } + + .tsd-member-summary-name:hover > .tsd-anchor-icon svg, + .tsd-anchor-link:hover > .tsd-anchor-icon svg, + .tsd-anchor-icon:focus-visible svg { + visibility: visible; + } + + .deprecated { + text-decoration: line-through !important; + } + + .warning { + padding: 1rem; + color: var(--color-warning-text); + background: var(--color-background-warning); + } + + .tsd-kind-project { + color: var(--color-ts-project); + } + .tsd-kind-module { + color: var(--color-ts-module); + } + .tsd-kind-namespace { + color: var(--color-ts-namespace); + } + .tsd-kind-enum { + color: var(--color-ts-enum); + } + .tsd-kind-enum-member { + color: var(--color-ts-enum-member); + } + .tsd-kind-variable { + color: var(--color-ts-variable); + } + .tsd-kind-function { + color: var(--color-ts-function); + } + .tsd-kind-class { + color: var(--color-ts-class); + } + .tsd-kind-interface { + color: var(--color-ts-interface); + } + .tsd-kind-constructor { + color: var(--color-ts-constructor); + } + .tsd-kind-property { + color: var(--color-ts-property); + } + .tsd-kind-method { + color: var(--color-ts-method); + } + .tsd-kind-reference { + color: var(--color-ts-reference); + } + .tsd-kind-call-signature { + color: var(--color-ts-call-signature); + } + .tsd-kind-index-signature { + color: var(--color-ts-index-signature); + } + .tsd-kind-constructor-signature { + color: var(--color-ts-constructor-signature); + } + .tsd-kind-parameter { + color: var(--color-ts-parameter); + } + .tsd-kind-type-parameter { + color: var(--color-ts-type-parameter); + } + .tsd-kind-accessor { + color: var(--color-ts-accessor); + } + .tsd-kind-get-signature { + color: var(--color-ts-get-signature); + } + .tsd-kind-set-signature { + color: var(--color-ts-set-signature); + } + .tsd-kind-type-alias { + color: var(--color-ts-type-alias); + } + + /* if we have a kind icon, don't color the text by kind */ + .tsd-kind-icon ~ span { + color: var(--color-text); + } + + /* mobile */ + @media (max-width: 769px) { + #tsd-toolbar-menu-trigger { + display: inline-block; + /* temporary fix to vertically align, for compatibility */ + line-height: 2.5; + } + #tsd-toolbar-links { + display: none; + } + + .container-main { + display: flex; + } + .col-content { + float: none; + max-width: 100%; + width: 100%; + } + .col-sidebar { + position: fixed !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + padding: 1.5rem 1.5rem 0 0; + width: 75vw; + visibility: hidden; + background-color: var(--color-background); + transform: translate(100%, 0); + } + .col-sidebar > *:last-child { + padding-bottom: 20px; + } + .overlay { + content: ""; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; + } + + .to-has-menu .overlay { + animation: fade-in 0.4s; + } + + .to-has-menu .col-sidebar { + animation: pop-in-from-right 0.4s; + } + + .from-has-menu .overlay { + animation: fade-out 0.4s; + } + + .from-has-menu .col-sidebar { + animation: pop-out-to-right 0.4s; + } + + .has-menu body { + overflow: hidden; + } + .has-menu .overlay { + visibility: visible; + } + .has-menu .col-sidebar { + visibility: visible; + transform: translate(0, 0); + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 100vh; + padding: 1rem 2rem; + } + .has-menu .tsd-navigation { + max-height: 100%; + } + .tsd-navigation .tsd-nav-link { + display: flex; + } + } + + /* one sidebar */ + @media (min-width: 770px) { + .container-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + grid-template-areas: "sidebar content"; + --dim-container-main-margin-y: 2rem; + } + + .tsd-breadcrumb { + margin-top: 0; + } + + .col-sidebar { + grid-area: sidebar; + } + .col-content { + grid-area: content; + padding: 0 1rem; + } + } + @media (min-width: 770px) and (max-width: 1399px) { + .col-sidebar { + max-height: calc( + 100vh - var(--dim-header-height) - var(--dim-footer-height) - + 2 * var(--dim-container-main-margin-y) + ); + overflow: auto; + position: sticky; + top: calc( + var(--dim-header-height) + var(--dim-container-main-margin-y) + ); + } + .site-menu { + margin-top: 1rem; + } + } + + /* two sidebars */ + @media (min-width: 1200px) { + .container-main { + grid-template-columns: + minmax(0, 1fr) minmax(0, 2.5fr) minmax( + 0, + 20rem + ); + grid-template-areas: "sidebar content toc"; + } + + .col-sidebar { + display: contents; + } + + .page-menu { + grid-area: toc; + padding-left: 1rem; + } + .site-menu { + grid-area: sidebar; + } + + .site-menu { + margin-top: 0rem; + } + + .page-menu, + .site-menu { + max-height: calc( + 100vh - var(--dim-header-height) - var(--dim-footer-height) - + 2 * var(--dim-container-main-margin-y) + ); + overflow: auto; + position: sticky; + top: calc( + var(--dim-header-height) + var(--dim-container-main-margin-y) + ); + } + } +} diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/discriminator.DiscriminatorRegistry.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/discriminator.DiscriminatorRegistry.html new file mode 100644 index 0000000..f197519 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/discriminator.DiscriminatorRegistry.html @@ -0,0 +1,156 @@ +DiscriminatorRegistry | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class DiscriminatorRegistry

                      +
                      +

                      Static registry for managing discriminators and their associated type guards. +Discriminators are used to identify the type of a Discriminated object and validate it using a registered type guard function.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Checks if a discriminator is already registered.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • discriminator: string +

                          The discriminator value to check.

                          +
                        +

                        Returns boolean

                          +
                        • true if the discriminator is registered, false otherwise.
                        • +
                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Validates an input against a specific discriminator.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • input: unknown +

                          The input to validate.

                          +
                        • +
                        • discriminator: string +

                          The discriminator value to check.

                          +
                        +

                        Returns boolean

                          +
                        • true if the input matches the type associated with the discriminator, false otherwise.
                        • +
                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/number.NumberUtility.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/number.NumberUtility.html new file mode 100644 index 0000000..99b163c --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/number.NumberUtility.html @@ -0,0 +1,129 @@ +NumberUtility | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class NumberUtility

                      +
                      +

                      Static properties and methods for validating number types.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Is the given input a finite number?

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • input: unknown
                        +

                        Returns input is number

                        true when the input is a finite number; false otherwise.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +

                        Is the given input an integer?

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • input: unknown
                        +

                        Returns input is number

                        true when the input is an integer; false otherwise.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +

                        Is the given input a positive integer?

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • input: unknown
                        • +
                        • zeroInclusive: boolean = false +

                          true if zero should be considered a valid input. +false if zero should be considered an invalid input. +Default value is false.

                          +
                        +

                        Returns input is number

                        true if the given input is a positive integer, or zero when zeroInclusive is true; false otherwise.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.Random.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.Random.html new file mode 100644 index 0000000..2676c7f --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.Random.html @@ -0,0 +1,305 @@ +Random | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class Random

                      +
                      +

                      Static properties and methods for generating random numbers and booleans, and for selecting random elements from arrays.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                        +
                      • +
                        set randomNumberGenerator(rng: () => number): void
                        +
                        +

                        Set the primary function used to generate random numbers.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • rng: () => number +

                          A function that returns a random number in the range [0, 1) (zero inclusive, one exclusive).

                          +
                        +

                        Returns void

                        +
                        +
                        +
                          +
                        • If the given random number generator is not a function.
                        • +
                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Returns number

                        A random number in the range [0, 1) (zero inclusive, one exclusive).

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +
                        +

                        Parameters

                        +
                          +
                        • chanceOfTrue: number = 0.5 +

                          The probability of returning true (between 0 and 1). +Default value is 0.5.

                          +
                        +

                        Returns boolean

                        A random boolean value.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +
                        +

                        Type Parameters

                        +
                          +
                        • Type
                        +
                        +

                        Parameters

                        +
                          +
                        • elements: Type[] +

                          An array of elements to choose from.

                          +
                        +

                        Returns Type

                        A random element from the array.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +
                        +

                        Parameters

                        +
                          +
                        • min: number +

                          The minimum value (inclusive).

                          +
                        • +
                        • max: number +

                          The maximum value (exclusive).

                          +
                        +

                        Returns number

                        A random floating-point number in the range [min, max) (min inclusive, max exclusive).

                        + +
                        +
                        +

                        When min is not a finite number.

                        +
                        +
                        +

                        When max is not a finite number.

                        +
                        +
                        +

                        When min is not less than or equal max.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +

                        If min or max is not an integer, it is rounded down with Math.floor before a value is generated.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • min: number +

                          The minimum value (inclusive). +Non-integer values are rounded down with Math.floor.

                          +
                        • +
                        • max: number +

                          The maximum value (exclusive). +Non-integer values are rounded down with Math.floor.

                          +
                        +

                        Returns number

                        A random integer in the range [Math.floor(min), Math.floor(max)) (min inclusive, max exclusive).

                        + +
                        +
                        +

                        When min is not a finite number.

                        +
                        +
                        +

                        When max is not a finite number.

                        +
                        +
                        +

                        When min is not less than or equal max.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +

                        If min or max is not an integer, it is rounded down with Math.floor before a value is generated.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • min: number +

                          The minimum value (inclusive). +Non-integer values are rounded down with Math.floor.

                          +
                        • +
                        • max: number +

                          The maximum value (exclusive). +Non-integer values are rounded down with Math.floor.

                          +
                        +

                        Returns number

                        A random integer in the range [Math.floor(min), Math.floor(max)) (min inclusive, max exclusive).

                        + +
                        + +
                        +

                        When min is not a finite number.

                        +
                        +
                        +

                        When max is not a finite number.

                        +
                        +
                        +

                        When min is not less than or equal max.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.RandomNumberGeneratorFactory.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.RandomNumberGeneratorFactory.html new file mode 100644 index 0000000..691a431 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.RandomNumberGeneratorFactory.html @@ -0,0 +1,155 @@ +RandomNumberGeneratorFactory | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class RandomNumberGeneratorFactory

                      +
                      +

                      A static factory class for creating a SeededRandomNumberGenerator object.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Build a SeededRandomNumberGenerator object with the given seed and namespace from an asynchronous hashing algorithm.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • seed: string +

                          The primary input to determine the random number sequence.

                          +
                        • +
                        • Optionalnamespace: string +

                          Namespace to create different sequences from the same seed.

                          +
                        +

                        Returns Promise<SeededRandomNumberGenerator>

                        +
                        +
                        +

                        This method relies on the Web Crypto API via crypto.subtle. +In Node.js environments, ensure you are using a version where the Web Crypto API is available.

                        +
                        +
                        +
                          +
                        • When the given seed is not a string.
                        • +
                        +
                        +
                        +
                          +
                        • When the given namespace is not a string.
                        • +
                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • + +
                        +

                        Build a SeededRandomNumberGenerator object with the given seed, namespace, and version.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • seed: string +

                          The primary input to determine the random number sequence.

                          +
                        • +
                        • Optionalnamespace: string +

                          Namespace to create different sequences from the same seed.

                          +
                        • +
                        • Optionalversion: number +

                          The SeedVersions index to use for selecting the offsets for hashing. +Changing the version number will result in a different sequence of random numbers for the same seed and namespace.

                          +
                        +

                        Returns SeededRandomNumberGenerator

                        +
                        + +
                        +
                          +
                        • When the given seed is not a string.
                        • +
                        +
                        +
                        +
                          +
                        • When the given namespace is not a string.
                        • +
                        +
                        +
                        +
                          +
                        • When the given version is not an integer.
                        • +
                        +
                        +
                        +
                          +
                        • When the given version is not a valid SeedVersions index.
                        • +
                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.SeedVersions.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.SeedVersions.html new file mode 100644 index 0000000..0f257af --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.SeedVersions.html @@ -0,0 +1,138 @@ +SeedVersions | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class SeedVersions

                      +
                      +

                      A static class for accessing different seed versions. +Each seed version index is guaranteed to always return the same seed version object, so that the same seed and version will always produce the same sequence of pseudorandom numbers.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Is the given index a valid seed version?

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • index: number +

                          The index to check.

                          +
                        +

                        Returns boolean

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.SeededRandomNumberGenerator.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.SeededRandomNumberGenerator.html new file mode 100644 index 0000000..f2d4928 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.SeededRandomNumberGenerator.html @@ -0,0 +1,121 @@ +SeededRandomNumberGenerator | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class SeededRandomNumberGenerator

                      +
                      +

                      Deterministic seeded pseudorandom number generator. +This generator utilizes the xoshiro128** algorithm, which is a pseudorandom number generator suitable for general-purpose use.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +
                        +

                        Parameters

                        +
                          +
                        • state: [number, number, number, number] +

                          Initial 128-bit state. +Must be an array with 4 32-bit unsigned integers, where at least one element is greater than 0.

                          +
                        +

                        Returns SeededRandomNumberGenerator

                        +
                        +
                        +

                        If state is not an array with 4 elements.

                        +
                        +
                        +

                        If each element of state is not a 32-bit unsigned integer.

                        +
                        +
                        +

                        If state does not have at least one element that is greater than 0.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Returns number

                          +
                        • The next pseudorandom float in the range [0, 1).
                        • +
                        + +
                        +
                        +

                        This method advances the internal 128-bit xoshiro128** state by one step. +Successive calls produce an independent, uniformly distributed sequence.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.WeightedElementUtility.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.WeightedElementUtility.html new file mode 100644 index 0000000..0950a6b --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/random.WeightedElementUtility.html @@ -0,0 +1,309 @@ +WeightedElementUtility | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class WeightedElementUtility

                      +
                      +

                      Static methods and properties for building and validating WeightedElement and WeightedList objects.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/string.ColorStringUtility.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/string.ColorStringUtility.html new file mode 100644 index 0000000..1b00d2a --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/string.ColorStringUtility.html @@ -0,0 +1,180 @@ +ColorStringUtility | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class ColorStringUtility

                      +
                      +

                      Static properties and methods for validating formatted color strings.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                        +
                      • +
                        get hexColorPattern(): RegExp
                        +
                        +

                        Returns RegExp

                        Regular expression pattern for validating hex color strings in the format #RRGGBB or #RRGGBBAA. +Case must be consistent in hex color strings: either all lowercase or all uppercase.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • +
                        get hexColorPatternRGB(): RegExp
                        +
                        +

                        Returns RegExp

                        Regular expression pattern for validating hex color strings in the format #RRGGBB. +Case must be consistent in hex color strings: either all lowercase or all uppercase.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • +
                        get hexColorPatternRGBA(): RegExp
                        +
                        +

                        Returns RegExp

                        Regular expression pattern for validating hex color strings in the format #RRGGBBAA. +Case must be consistent in hex color strings: either all lowercase or all uppercase.

                        + +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/string.StringUtility.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/string.StringUtility.html new file mode 100644 index 0000000..29a3c26 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/classes/string.StringUtility.html @@ -0,0 +1,233 @@ +StringUtility | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Class StringUtility

                      +
                      +

                      Static properties and methods for validating string types.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                        +
                      • +
                        get singleLineLowercaseTrimmedPattern(): RegExp
                        +
                        +

                        Returns RegExp

                        Regular expression pattern for validating single-line lowercase strings.

                        + +
                        +
                        +

                        This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • +
                        get singleLineTrimmedPattern(): RegExp
                        +
                        +

                        Returns RegExp

                        Regular expression pattern for validating single-line mixed-case strings.

                        + +
                        +
                        +

                        This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                        +
                      • +
                        get singleLineUppercaseTrimmedPattern(): RegExp
                        +
                        +

                        Returns RegExp

                        Regular expression pattern for validating single-line uppercase strings.

                        + +
                        +
                        +

                        This expression does not allow tab breaks, new lines, leading whitespace, trailing whitespace, or consecutive spaces within the string.

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Is the given input a non-empty string? +Non-empty strings must contain at least one non-whitespace character.

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • input: unknown
                        +

                        Returns input is string

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                        +
                      • + +
                        +

                        Is the given input a string?

                        +
                        +
                        +

                        Parameters

                        +
                          +
                        • input: unknown
                        +

                        Returns input is string

                        +
                        +
                        +

                        0.1.0

                        +
                      +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/enums/discriminator.Discriminators.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/enums/discriminator.Discriminators.html new file mode 100644 index 0000000..dc8887d --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/enums/discriminator.Discriminators.html @@ -0,0 +1,73 @@ +Discriminators | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Enumeration Discriminators

                      +
                      +

                      Valid discriminators for package types and interfaces.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      WeightedElement: "@blwatkins/utils:WeightedElement"
                      +

                      Discriminator value for the WeightedElement interface.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/hierarchy.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/hierarchy.html new file mode 100644 index 0000000..2c8774d --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/hierarchy.html @@ -0,0 +1,34 @@ +@blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      +

                      @blwatkins/utils - v0.1.0-alpha.1

                      +

                      Hierarchy Summary

                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/index.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/index.html new file mode 100644 index 0000000..92b6bec --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/index.html @@ -0,0 +1,113 @@ +@blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      +

                      @blwatkins/utils - v0.1.0-alpha.1

                      +

                      TypeScript Utilities

                      +

                      A growing toolkit of reusable, domain-agnostic TypeScript and JavaScript utilities for everyday development.

                      + + + +

                      Development for this project originally began in March 2022 when I started creating utility classes in JavaScript to use across my personal algorithmic generative art projects. +Over the years, the utilities have been refined, expanded, updated, and organized into a series of cohesive libraries that can be used in a variety of projects.

                      + +

                      The source code of this project is licensed under the MIT License. +The full text of the license is included with the project source code.

                      + + +

                      npm License +npm Version +npm @alpha Version +npm Types +npm Node Version +npm Weekly Downloads +npm Total Downloads +npm Last Update +npm Unpacked Size

                      + +

                      Socket Score

                      + +

                      Bundlephobia Tree Shaking +Bundlephobia Dependency Count +Bundlephobia Minified +Bundlephobia Minified + gzip

                      + +

                      Package Phobia Install Size +Package Phobia Publish Size

                      + +

                      GitHub License +GitHub Dependabot +GitHub Latest Release +GitHub Releases +GitHub Last Commit +GitHub Commits +GitHub Commit Activity +GitHub Code Size in Bytes +GitHub Repo Size +GitHub Repo File or Directory Count +GitHub Language Count

                      + +

                      CodeQL +npm Lint, Build, and Test +Deploy GitHub Pages with Jekyll

                      + + + +

                      A huge thank you to all the open source contributors who have made this project possible by creating and maintaining the libraries and tools used in this project, and to the open source community for fostering collaboration and innovation.

                      +

                      A special thank you to all the educators, mentors, and content creators who have shared their knowledge and expertise in the fields of algorithmic art, web development, and computer science. +Thank you for giving me the tools, resources, opportunities, support, and inspiration to learn and grow as a developer.

                      +
                      +

                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/discriminator.DiscriminatorRegistration.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/discriminator.DiscriminatorRegistration.html new file mode 100644 index 0000000..840a54a --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/discriminator.DiscriminatorRegistration.html @@ -0,0 +1,104 @@ +DiscriminatorRegistration | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Interface DiscriminatorRegistration

                      +
                      +

                      A registration for a discriminator to the DiscriminatorRegistry.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      interface DiscriminatorRegistration {
                          discriminator: string;
                          validator: (input: unknown) => boolean;
                      }
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      discriminator: string
                      +

                      The discriminator value that identifies the type of a Discriminated object. +This value must be unique across all registered discriminators.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      validator: (input: unknown) => boolean
                      +

                      A method that validates whether an input matches the type associated with the discriminator.

                      +
                      +
                      +

                      Type Declaration

                      +
                        +
                      • +
                          +
                        • (input: unknown): boolean
                        • +
                        • +
                          +

                          Parameters

                          +
                            +
                          • input: unknown +

                            The input to validate.

                            +
                          +

                          Returns boolean

                            +
                          • true if the input matches the type associated with the discriminator, false otherwise.
                          • +
                          +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/random.SeedVersion.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/random.SeedVersion.html new file mode 100644 index 0000000..bdee372 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/random.SeedVersion.html @@ -0,0 +1,76 @@ +SeedVersion | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Interface SeedVersion

                      +
                      +

                      A seed version defines a specific set of offsets for the FNV-1a hashing algorithm. +A different set of offsets results in a different hash for the same input, which results in a different initial state for the seeded random number generator, which results in a different sequence of pseudorandom numbers.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      interface SeedVersion {
                          offsets: readonly [number, number, number, number];
                      }
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      offsets: readonly [number, number, number, number]
                      +

                      A collection of offset values for the FNV-1a hashing algorithm. +Each offset value should be a 32-bit unsigned integer.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/random.WeightedElement.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/random.WeightedElement.html new file mode 100644 index 0000000..339b004 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/interfaces/random.WeightedElement.html @@ -0,0 +1,103 @@ +WeightedElement | @blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      + +

                      Interface WeightedElement<TValue>

                      +
                      +

                      Interface for a weighted element, which can be used for non-uniform random selection from a list.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      interface WeightedElement<TValue> {
                          discriminator: WeightedElement;
                          value: TValue;
                          weight: number;
                      }
                      +
                      +

                      Type Parameters

                      +
                        +
                      • TValue
                      +
                      +
                      +
                      +
                      Index
                      +
                      +
                      + +
                      +
                      + +
                      +
                      + +
                      discriminator: WeightedElement
                      +

                      The discriminator for the weighted element.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      value: TValue
                      +

                      The value to be selected from the weighted list.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      weight: number
                      +

                      The probability weight of the element. +Should be a number between 0 and 1, inclusive.

                      +
                      +
                      +
                      +

                      0.1.0

                      +
                      +
                      + +
                      +

                      Generated using TypeDoc

                      +


                      Copyright © 2022-2026 Brittni Watkins.

                      +
                      diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules.html new file mode 100644 index 0000000..a4e5747 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules.html @@ -0,0 +1,41 @@ +@blwatkins/utils - v0.1.0-alpha.1
                      +
                      +
                      +
                      +
                      +
                        +

                        @blwatkins/utils - v0.1.0-alpha.1

                        +
                        +
                        discriminator
                        number
                        random
                        string
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/discriminator.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/discriminator.html new file mode 100644 index 0000000..5e478d5 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/discriminator.html @@ -0,0 +1,58 @@ +discriminator | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Module discriminator

                        +
                        +
                        Discriminators
                        +
                        +
                        DiscriminatorRegistry
                        +
                        +
                        DiscriminatorRegistration
                        +
                        +
                        Discriminated
                        TypeGuard
                        +
                        +
                        discriminatedSchema
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/number.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/number.html new file mode 100644 index 0000000..69b59a0 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/number.html @@ -0,0 +1,42 @@ +number | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Module number

                        +
                        +
                        NumberUtility
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/random.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/random.html new file mode 100644 index 0000000..73ac3ca --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/random.html @@ -0,0 +1,54 @@ +random | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        + +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/string.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/string.html new file mode 100644 index 0000000..c3126f8 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/modules/string.html @@ -0,0 +1,42 @@ +string | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Module string

                        +
                        +
                        ColorStringUtility
                        StringUtility
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/discriminator.Discriminated.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/discriminator.Discriminated.html new file mode 100644 index 0000000..5b9e42f --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/discriminator.Discriminated.html @@ -0,0 +1,45 @@ +Discriminated | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Type Alias Discriminated

                        +
                        Discriminated: Static<typeof discriminatedSchema>
                        +

                        Discriminated objects can be type checked using the discriminator registry.

                        +
                        +
                        +
                        +

                        0.1.0

                        +
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/discriminator.TypeGuard.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/discriminator.TypeGuard.html new file mode 100644 index 0000000..317b872 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/discriminator.TypeGuard.html @@ -0,0 +1,61 @@ +TypeGuard | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Type Alias TypeGuard<T>

                        +
                        TypeGuard: (input: unknown) => input is T
                        +

                        A type guard function that checks if an input is of a specific Discriminated type.

                        +
                        +
                        +

                        Type Parameters

                        +
                        +
                        +

                        Type Declaration

                        +
                          +
                        • +
                            +
                          • (input: unknown): input is T
                          • +
                          • +
                            +

                            Parameters

                            +
                              +
                            • input: unknown
                            +

                            Returns input is T

                        +
                        +
                        +

                        0.1.0

                        +
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/random.WeightedList.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/random.WeightedList.html new file mode 100644 index 0000000..8b8f7d3 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/types/random.WeightedList.html @@ -0,0 +1,52 @@ +WeightedList | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Type Alias WeightedList<TValue>

                        +
                        WeightedList: WeightedElement<TValue>[]
                        +

                        Type alias for a list of WeightedElement objects.

                        +
                        +
                        +

                        Type Parameters

                        +
                          +
                        • TValue
                        +
                        + +
                        +

                        0.1.0

                        +
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/variables/discriminator.discriminatedSchema.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/variables/discriminator.discriminatedSchema.html new file mode 100644 index 0000000..b81f3a7 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/variables/discriminator.discriminatedSchema.html @@ -0,0 +1,45 @@ +discriminatedSchema | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Variable discriminatedSchemaConst

                        +
                        discriminatedSchema: TObject<{ discriminator: TReadonly<TString> }> = ...
                        +

                        TypeBox schema for validating that an object implements the Discriminated type.

                        +
                        +
                        +
                        +

                        0.1.0

                        +
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/variables/random.weightedElementSchema.html b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/variables/random.weightedElementSchema.html new file mode 100644 index 0000000..442cbe8 --- /dev/null +++ b/docs/releases/v0.x/v0.1.x/v0.1.0-alpha.x/v0.1.0-alpha.1/doc/variables/random.weightedElementSchema.html @@ -0,0 +1,45 @@ +weightedElementSchema | @blwatkins/utils - v0.1.0-alpha.1
                        +
                        +
                        +
                        +
                        + +

                        Variable weightedElementSchemaConst

                        +
                        weightedElementSchema: TGeneric<
                            [TParameter<"T", TUnknown, TUnknown>],
                            TIntersect<
                                [
                                    TObject<{ discriminator: TReadonly<TString> }>,
                                    TObject<
                                        {
                                            discriminator: TLiteral<WeightedElement>;
                                            value: TReadonly<TRef<"T">>;
                                            weight: TReadonly<TNumber>;
                                        },
                                    >,
                                ],
                            >,
                        > = ...
                        +

                        TypeBox schema to validate a WeightedElement object.

                        +
                        +
                        +
                        +

                        0.1.0

                        +
                        +
                        + +
                        +

                        Generated using TypeDoc

                        +


                        Copyright © 2022-2026 Brittni Watkins.

                        +
                        diff --git a/docs/resources-and-references.md b/docs/resources-and-references.md index 138fe7f..5ec7325 100644 --- a/docs/resources-and-references.md +++ b/docs/resources-and-references.md @@ -1,10 +1,10 @@ --- -title: "TypeScript Utilities - Resources and References" +title: "Resources and References" +layout: post author: - Brittni Watkins -layout: post date: 2026-06-05 -modified_date: 2026-06-05 +modified_date: 2026-07-08 toc: true --- @@ -12,10 +12,14 @@ toc: true ### JavaScript -[MDN Web Docs - Number.isFinite()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) +- [MDN Web Docs - Array.prototype.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every) +- [MDN Web Docs - Number.isFinite()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) +- [MDN Web Docs - Number.isInteger()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger) + +### TypeScript -[MDN Web Docs - Number.isInteger()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger) +- [TypeScript Reference - Enums](https://www.typescriptlang.org/docs/handbook/enums.html) ## Pseudorandom Number Generation -[Xoshiro128** Algorithm](https://github.com/bryc/code/blob/master/jshash/PRNGs.md#xoshiro) +- [Xoshiro128** Algorithm](https://github.com/bryc/code/blob/master/jshash/PRNGs.md#xoshiro) diff --git a/package-lock.json b/package-lock.json index 3052322..e9f41fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,103 +1,35 @@ { "name": "@blwatkins/utils", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@blwatkins/utils", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "license": "MIT", "dependencies": { - "typebox": "^1.3.2" + "typebox": "^1.3.6" }, "devDependencies": { "@eslint/js": "^10.0.1", "@stylistic/eslint-plugin": "^5.10.0", - "@types/node": "^26.1.0", - "@vitest/coverage-v8": "^4.1.9", - "@vitest/ui": "^4.1.9", + "@types/node": "^26.1.1", + "@vitest/coverage-v8": "^4.1.10", + "@vitest/ui": "^4.1.10", "eslint": "^10.6.0", "eslint-plugin-es-x": "^9.7.0", "globals": "^17.7.0", - "tsdown": "^0.22.3", - "typedoc": "^0.28.19", + "tsdown": "^0.22.4", + "typedoc": "^0.28.20", "typescript": "^6.0.3", - "typescript-eslint": "^8.62.1", - "vitest": "^4.1.9" + "typescript-eslint": "^8.63.0", + "vitest": "^4.1.10" }, "engines": { "node": "^22.22.0 || >=24" } }, - "node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/generator/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/generator/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/generator/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/@babel/generator/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", @@ -400,17 +332,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -459,9 +380,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.138.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz", - "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==", + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", "dev": true, "license": "MIT", "funding": { @@ -489,9 +410,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz", - "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", "cpu": [ "arm64" ], @@ -506,9 +427,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz", - "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", "cpu": [ "arm64" ], @@ -523,9 +444,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz", - "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", "cpu": [ "x64" ], @@ -540,9 +461,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz", - "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", "cpu": [ "x64" ], @@ -557,9 +478,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz", - "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", "cpu": [ "arm" ], @@ -574,9 +495,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz", - "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", "cpu": [ "arm64" ], @@ -594,9 +515,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz", - "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", "cpu": [ "arm64" ], @@ -614,9 +535,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz", - "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", "cpu": [ "ppc64" ], @@ -634,9 +555,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz", - "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", "cpu": [ "s390x" ], @@ -654,9 +575,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz", - "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", "cpu": [ "x64" ], @@ -674,9 +595,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz", - "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", "cpu": [ "x64" ], @@ -694,9 +615,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz", - "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", "cpu": [ "arm64" ], @@ -711,9 +632,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz", - "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", "cpu": [ "wasm32" ], @@ -730,9 +651,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz", - "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", "cpu": [ "arm64" ], @@ -747,9 +668,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz", - "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", "cpu": [ "x64" ], @@ -900,13 +821,6 @@ "@types/unist": "*" } }, - "node_modules/@types/jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -915,9 +829,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", - "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "dev": true, "license": "MIT", "dependencies": { @@ -932,17 +846,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz", - "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz", + "integrity": "sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/type-utils": "8.62.1", - "@typescript-eslint/utils": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/scope-manager": "8.63.0", + "@typescript-eslint/type-utils": "8.63.0", + "@typescript-eslint/utils": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -955,7 +869,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.62.1", + "@typescript-eslint/parser": "^8.63.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -971,16 +885,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz", - "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.63.0.tgz", + "integrity": "sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/scope-manager": "8.63.0", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/typescript-estree": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0", "debug": "^4.4.3" }, "engines": { @@ -996,14 +910,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz", - "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz", + "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.62.1", - "@typescript-eslint/types": "^8.62.1", + "@typescript-eslint/tsconfig-utils": "^8.63.0", + "@typescript-eslint/types": "^8.63.0", "debug": "^4.4.3" }, "engines": { @@ -1018,14 +932,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz", - "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz", + "integrity": "sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1" + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1036,9 +950,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz", - "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz", + "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==", "dev": true, "license": "MIT", "engines": { @@ -1053,15 +967,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz", - "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz", + "integrity": "sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/utils": "8.62.1", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/typescript-estree": "8.63.0", + "@typescript-eslint/utils": "8.63.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1078,9 +992,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz", - "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.63.0.tgz", + "integrity": "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==", "dev": true, "license": "MIT", "engines": { @@ -1092,16 +1006,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz", - "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz", + "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.62.1", - "@typescript-eslint/tsconfig-utils": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/project-service": "8.63.0", + "@typescript-eslint/tsconfig-utils": "8.63.0", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1120,16 +1034,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz", - "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.63.0.tgz", + "integrity": "sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1" + "@typescript-eslint/scope-manager": "8.63.0", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/typescript-estree": "8.63.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1144,13 +1058,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz", - "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz", + "integrity": "sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/types": "8.63.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1175,14 +1089,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", - "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -1196,8 +1110,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.9", - "vitest": "4.1.9" + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1206,16 +1120,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1224,13 +1138,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1251,9 +1165,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1264,13 +1178,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -1278,14 +1192,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1294,9 +1208,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -1304,13 +1218,13 @@ } }, "node_modules/@vitest/ui": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.9.tgz", - "integrity": "sha512-U/cRvtqfEPj27FI1n9cyUvi4vXXdcLhjJiI+InYKdk8hP4VrS6RXOjGL7rfFaeBc37iRKANsR6eEzIoC7lmgBQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.10.tgz", + "integrity": "sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "fflate": "^0.8.2", "flatted": "^3.4.2", "pathe": "^2.0.3", @@ -1322,17 +1236,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.9" + "vitest": "4.1.10" } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -1340,6 +1254,335 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@yuku-codegen/binding-darwin-arm64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-darwin-arm64/-/binding-darwin-arm64-0.5.44.tgz", + "integrity": "sha512-mpZc7hrjl/qxcbEMS6vVLE0lO4DjiXCvrp3gYbZ58bbmsSxSGCTlOCA0lpeLXAKOZxe0ZrVoqKteaewFF2Vbuw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-codegen/binding-darwin-x64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-darwin-x64/-/binding-darwin-x64-0.5.44.tgz", + "integrity": "sha512-PCt776PnGtnQYimxk18IyvPf/BQ6CNU95W+6eaoQfeME8ra+7v3pNNoTAmLfSveUC9KZPaoajR9NqkKfEVjA2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-codegen/binding-freebsd-x64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-freebsd-x64/-/binding-freebsd-x64-0.5.44.tgz", + "integrity": "sha512-5MNu1R4ysytPLMdl1UlGyjgAbUdT8fguW/QRo+pyEymbuWRN5n8JEHCFpm4AsWdP61fStagSpPDJcwN+mwC9Lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm-gnu": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.5.44.tgz", + "integrity": "sha512-xbP2qQ7/h6ZZKeckCiI2osB5SE5PBfLb4uzIfO1BSTX+9rlBKTqomxDaCib7aPf2X1CXMiIq+i7AK1EhBa5a7A==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm-musl": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm-musl/-/binding-linux-arm-musl-0.5.44.tgz", + "integrity": "sha512-gmXKMCpgkUm5PllGMKBMoBmqgbTQkx+S85eE46LctuwTSKS/K7u65NE6t4zk6cLDvZpV67enycKXBORWn6q7xA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm64-gnu": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.5.44.tgz", + "integrity": "sha512-eOoejNUtnHs1/TMn4wryOAG/TarvlOqSZtRPeS56liBKp/GVQSirqTM9AITSGPLSdK1u7fZWMdj5IOEoJp3ruw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm64-musl": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.5.44.tgz", + "integrity": "sha512-xTzrhEMy1mdv5+SbJPPlwqlMrhXGPntE2f12TLi+dNPXhif4iXCGJpoh8pV9nwZFE/cq1ITuTnjIfTFj/PifzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-x64-gnu": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.5.44.tgz", + "integrity": "sha512-2bvgIU4P+f4q18grdHoGnt9L6XlAciq/8GWpM06fmwj8qqruS8qwwwZXBk3/0U2+IHGv9pXRR8GtSXzl5n/blg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-x64-musl": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-x64-musl/-/binding-linux-x64-musl-0.5.44.tgz", + "integrity": "sha512-EzX5hWK0MmU4fbqY9coc5PJ0y0LYjrBdAF7mjSyetdK/yWRGBfHi9nh0dUJ0lqb47653hxB8/r9byfYgg6ecbA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-win32-arm64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-win32-arm64/-/binding-win32-arm64-0.5.44.tgz", + "integrity": "sha512-/ajGFWcOLGtmVUdxWKXDW6Ixtez68xxtmd2l95xNg8Lzs4aqbV2cy0Ns2Bzg9vjHsgDIooQlLXHpWh6HsHcy6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-codegen/binding-win32-x64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-win32-x64/-/binding-win32-x64-0.5.44.tgz", + "integrity": "sha512-geuJQ7FKI8YUtBgW8w72ChMfMvYy3gSytACxB4HOkylsoutrhH6lUNYHk1ny/p8u8t47NGxktpnzVJzI8IzJkA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-parser/binding-darwin-arm64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.5.44.tgz", + "integrity": "sha512-EXSW5w1YOIMyxu53MFxP43gTDeHlQueOjk8AEI9tuLF5976bDq3MXuIgzQvZKPVAirxGZu8+ANVXH1WcAH1G6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-parser/binding-darwin-x64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-darwin-x64/-/binding-darwin-x64-0.5.44.tgz", + "integrity": "sha512-pJBHsKxqR/nPwwaXgkkYTVo3G9dJ/AXhWsYyKyadU1zLg9gGfVwVTMehqwwutMCONDsLqOmEv/UqItmhpVsQJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-parser/binding-freebsd-x64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.5.44.tgz", + "integrity": "sha512-fOqYX5BQML94uj9hd3a+LlBNz54OoDm6l+wgUIZ8UUkOBfPV+w2lux1jj9FKXT85wwjDOFhgZ/XQYSEDK/N9mA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm-gnu": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.5.44.tgz", + "integrity": "sha512-kA2jRNh2cbbHDbhBN9IR8CnYI4UZHNQg0OVz6+V16Ph5VlLYpfv/6GdCmvQQoTGAnhFlbQiRoQys03ey91FTew==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm-musl": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm-musl/-/binding-linux-arm-musl-0.5.44.tgz", + "integrity": "sha512-TQ82L8c5Lxl3HBOmw0uGfCcsyw0mp7DVGwCuW24OdDWak6BKaumvB8/Ep1llPOvhk5xgSFB7fsqgh6sIwkNRew==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm64-gnu": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.5.44.tgz", + "integrity": "sha512-o6up+m/MsoMEtq6h4JTzmzKOvH9o3o41vK8oiok0LOZoPOFOqOSqC+N5V2ArD1O54m5LUq6ErghAbGWiPsMsqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm64-musl": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.5.44.tgz", + "integrity": "sha512-3XZoiHhtrjWKgk7CJC/sGzk1TE57SDPYzOFXUg6CZrigRZ+c0Q5ItzJpyAvhzlxv5ruNRYiuGvFbaRSIDnp2BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-x64-gnu": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.5.44.tgz", + "integrity": "sha512-7KYCySZI6cjsdeiiIwvviDfJFd4Xj5gjNBzm9akUpwxRXNwaHHuEq2yGkVdGqj3BT6dJsiNhJIhtS6k7/HJdFA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-x64-musl": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.5.44.tgz", + "integrity": "sha512-SLU/nXwOjET2XlOiO984sAzCloiWw0+JdcWpGzXLz3JQWbdiblxWC9cIvB0fCtKdDhX1mIutKNEH+RRRJADYcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-win32-arm64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-win32-arm64/-/binding-win32-arm64-0.5.44.tgz", + "integrity": "sha512-LGmaJtB2mVqPD7Gu/RKAu9aIVxlaKPOgKB8RPHeFFD5Tf/apCiWZix1tkBjdOAo9cVEAoR9qnVHE6zJ+OG0drQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-parser/binding-win32-x64": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-win32-x64/-/binding-win32-x64-0.5.44.tgz", + "integrity": "sha512-2i0FTklLuhBIgILvtEQ3IN3J4qaTMKlxptseWNrz4F5mimL9UpQFUniVju9OOInwP2O1vgLdZn8EBjEXGNCHmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-toolchain/types": { + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.5.43.tgz", + "integrity": "sha512-kSpvPntnXw5+lYjO71ffBEnQ5ycQ74KGIYknh0TS4xeyCuBkOqxyJumxZkMhLBBUCLjDAbx2+Icnr3Zh4ftjpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", @@ -1407,74 +1650,6 @@ "node": ">=12" } }, - "node_modules/ast-kit": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-3.0.0.tgz", - "integrity": "sha512-8OG92q3R35qjC/4i6BLBMg8IB+fClWu/1PEwg2Z9Rn+BuNaiEgJzpzn+pxWOdHJWDCAwu2JP0wCDTozAM4QirQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "estree-walker": "^3.0.3", - "pathe": "^2.0.3" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/ast-kit/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/ast-kit/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/ast-kit/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/ast-kit/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, "node_modules/ast-v8-to-istanbul": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", @@ -1497,16 +1672,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/birpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz", - "integrity": "sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/brace-expansion": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", @@ -2202,19 +2367,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -2906,13 +3058,13 @@ } }, "node_modules/rolldown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", - "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.138.0", + "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -2922,38 +3074,36 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.4", - "@rolldown/binding-darwin-arm64": "1.1.4", - "@rolldown/binding-darwin-x64": "1.1.4", - "@rolldown/binding-freebsd-x64": "1.1.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", - "@rolldown/binding-linux-arm64-gnu": "1.1.4", - "@rolldown/binding-linux-arm64-musl": "1.1.4", - "@rolldown/binding-linux-ppc64-gnu": "1.1.4", - "@rolldown/binding-linux-s390x-gnu": "1.1.4", - "@rolldown/binding-linux-x64-gnu": "1.1.4", - "@rolldown/binding-linux-x64-musl": "1.1.4", - "@rolldown/binding-openharmony-arm64": "1.1.4", - "@rolldown/binding-wasm32-wasi": "1.1.4", - "@rolldown/binding-win32-arm64-msvc": "1.1.4", - "@rolldown/binding-win32-x64-msvc": "1.1.4" + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" } }, "node_modules/rolldown-plugin-dts": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.26.0.tgz", - "integrity": "sha512-e+kEPtUiDES0htk5iqkSeF4EzAV7R+vugGB44iPDuw1Kw9E+WyL1VG7PaV0IIjGHLiacztMBcMTyrr8ON9CT1Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.27.2.tgz", + "integrity": "sha512-BzyD3qqF4DAg12c8QTVia73dXReDcsMsYHPvKxxjCeBolDbVxtYIy+YanF6osBLn5WGp2F43McOSUhxplgEZuQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/generator": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0", - "@babel/parser": "^8.0.0", - "ast-kit": "^3.0.0", - "birpc": "^4.0.0", "dts-resolver": "^3.0.0", "get-tsconfig": "5.0.0-beta.5", - "obug": "^2.1.3" + "obug": "^2.1.3", + "yuku-ast": "^0.1.7", + "yuku-codegen": "^0.5.44", + "yuku-parser": "^0.5.44" }, "engines": { "node": "^22.18.0 || >=24.11.0" @@ -2965,7 +3115,7 @@ "@ts-macro/tsc": "^0.3.6", "@typescript/native-preview": ">=7.0.0-dev.20260325.1", "rolldown": "^1.0.0", - "typescript": "^5.0.0 || ^6.0.0", + "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": "~3.2.0 || ~3.3.0" }, "peerDependenciesMeta": { @@ -2983,56 +3133,6 @@ } } }, - "node_modules/rolldown-plugin-dts/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/rolldown-plugin-dts/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz", - "integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/rolldown-plugin-dts/node_modules/@babel/parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz", - "integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/rolldown-plugin-dts/node_modules/@babel/types": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz", - "integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.0" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -3109,9 +3209,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, @@ -3206,9 +3306,9 @@ } }, "node_modules/tsdown": { - "version": "0.22.3", - "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.22.3.tgz", - "integrity": "sha512-louqbfA8Qf//B9jTTL0FPtXTNpjCWv1VPkbcmQMph2pTpzs+LnB1tbe4tDDRVpo2BjF5SgUXaTZe45SxB8pWHg==", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.22.4.tgz", + "integrity": "sha512-3a5FsNL2fH2jw3ozvFUuPMBgS0xXjX9wpZShHyB4klXelVhyaNw5Q5WA9TPCNeoGYpRZEc4OZdMx5wT4Fkma3A==", "dev": true, "license": "MIT", "dependencies": { @@ -3219,10 +3319,10 @@ "hookable": "^6.1.1", "import-without-cache": "^0.4.0", "obug": "^2.1.3", - "picomatch": "^4.0.4", - "rolldown": "~1.1.1", - "rolldown-plugin-dts": "^0.26.0", - "semver": "^7.8.4", + "picomatch": "^4.0.5", + "rolldown": "~1.1.4", + "rolldown-plugin-dts": "^0.27.1", + "semver": "^7.8.5", "tinyexec": "^1.2.4", "tinyglobby": "^0.2.17", "tree-kill": "^1.2.2", @@ -3239,8 +3339,8 @@ }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.22.3", - "@tsdown/exe": "0.22.3", + "@tsdown/css": "0.22.4", + "@tsdown/exe": "0.22.4", "@vitejs/devtools": "*", "publint": "^0.3.8", "tsx": "*", @@ -3300,23 +3400,23 @@ } }, "node_modules/typebox": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.3.tgz", - "integrity": "sha512-URXGUE31PJDQC+PtRMJeLdF4kmmOdFoVPikPCtV2oOIhUpNpppEdIz7W8bH8cFYPYHdDpaRvqwdegMTmHliudg==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.6.tgz", + "integrity": "sha512-Sc8RA0NCMEFmApHNU9ZMzqcpQj46She44J8ffpLM/bdhLNUZKq7DJumcLcsFx1gRmDfQPgCgOmFFJ7rcnfWNyA==", "license": "MIT" }, "node_modules/typedoc": { - "version": "0.28.19", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.19.tgz", - "integrity": "sha512-wKh+lhdmMFivMlc6vRRcMGXeGEHGU2g8a2CkPTJjJlwRf1iXbimWIPcFolCqe4E0d/FRtGszpIrsp3WLpDB8Pw==", + "version": "0.28.20", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz", + "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@gerrit0/mini-shiki": "^3.23.0", "lunr": "^2.3.9", - "markdown-it": "^14.1.1", + "markdown-it": "^14.3.0", "minimatch": "^10.2.5", - "yaml": "^2.8.3" + "yaml": "^2.9.0" }, "bin": { "typedoc": "bin/typedoc" @@ -3344,16 +3444,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.1.tgz", - "integrity": "sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==", + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.63.0.tgz", + "integrity": "sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.62.1", - "@typescript-eslint/parser": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/utils": "8.62.1" + "@typescript-eslint/eslint-plugin": "8.63.0", + "@typescript-eslint/parser": "8.63.0", + "@typescript-eslint/typescript-estree": "8.63.0", + "@typescript-eslint/utils": "8.63.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3484,19 +3584,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -3524,12 +3624,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -3644,6 +3744,65 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yuku-ast": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/yuku-ast/-/yuku-ast-0.1.7.tgz", + "integrity": "sha512-2RiMEWv500TixY5rJy6OZd4fSy9WYZKWh6gGbIJ7y7vAGcuCugWOWwOLGaQcRZrXcPUfqtLtvpaJ3SdXtWlhKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "0.5.43" + }, + "funding": { + "url": "https://github.com/sponsors/arshad-yaseen" + } + }, + "node_modules/yuku-codegen": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/yuku-codegen/-/yuku-codegen-0.5.44.tgz", + "integrity": "sha512-0rhtgWGz+bR3Pe7xqJ5E4VqxI1vqNtkeJRkeXM0Qd3tgldYClQipxa9bRZyuNOOfk0Ri02scrjnkoAHM16/f2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "0.5.43" + }, + "optionalDependencies": { + "@yuku-codegen/binding-darwin-arm64": "0.5.44", + "@yuku-codegen/binding-darwin-x64": "0.5.44", + "@yuku-codegen/binding-freebsd-x64": "0.5.44", + "@yuku-codegen/binding-linux-arm-gnu": "0.5.44", + "@yuku-codegen/binding-linux-arm-musl": "0.5.44", + "@yuku-codegen/binding-linux-arm64-gnu": "0.5.44", + "@yuku-codegen/binding-linux-arm64-musl": "0.5.44", + "@yuku-codegen/binding-linux-x64-gnu": "0.5.44", + "@yuku-codegen/binding-linux-x64-musl": "0.5.44", + "@yuku-codegen/binding-win32-arm64": "0.5.44", + "@yuku-codegen/binding-win32-x64": "0.5.44" + } + }, + "node_modules/yuku-parser": { + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/yuku-parser/-/yuku-parser-0.5.44.tgz", + "integrity": "sha512-mAhpQZ/bXjxZmKiGUqEWskC9mZTcTBv6/fdzVdzdjM6XuD1DP3IavdLVWdM39L9ewK9vS9OtJmaKNeWgRzpy0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "0.5.43" + }, + "optionalDependencies": { + "@yuku-parser/binding-darwin-arm64": "0.5.44", + "@yuku-parser/binding-darwin-x64": "0.5.44", + "@yuku-parser/binding-freebsd-x64": "0.5.44", + "@yuku-parser/binding-linux-arm-gnu": "0.5.44", + "@yuku-parser/binding-linux-arm-musl": "0.5.44", + "@yuku-parser/binding-linux-arm64-gnu": "0.5.44", + "@yuku-parser/binding-linux-arm64-musl": "0.5.44", + "@yuku-parser/binding-linux-x64-gnu": "0.5.44", + "@yuku-parser/binding-linux-x64-musl": "0.5.44", + "@yuku-parser/binding-win32-arm64": "0.5.44", + "@yuku-parser/binding-win32-x64": "0.5.44" + } } } } diff --git a/package.json b/package.json index eefaffb..b74c333 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blwatkins/utils", - "version": "0.1.0-alpha.0", + "version": "0.1.0-alpha.1", "license": "MIT", "private": false, "description": "A growing toolkit of reusable, domain-agnostic TypeScript and JavaScript utilities for everyday development.", @@ -48,22 +48,22 @@ "prepack": "npm run build" }, "dependencies": { - "typebox": "^1.3.2" + "typebox": "^1.3.6" }, "devDependencies": { "@eslint/js": "^10.0.1", "@stylistic/eslint-plugin": "^5.10.0", - "@types/node": "^26.1.0", - "@vitest/coverage-v8": "^4.1.9", - "@vitest/ui": "^4.1.9", + "@types/node": "^26.1.1", + "@vitest/coverage-v8": "^4.1.10", + "@vitest/ui": "^4.1.10", "eslint": "^10.6.0", "eslint-plugin-es-x": "^9.7.0", "globals": "^17.7.0", - "tsdown": "^0.22.3", - "typedoc": "^0.28.19", + "tsdown": "^0.22.4", + "typedoc": "^0.28.20", "typescript": "^6.0.3", - "typescript-eslint": "^8.62.1", - "vitest": "^4.1.9" + "typescript-eslint": "^8.63.0", + "vitest": "^4.1.10" }, "keywords": [ "typescript", @@ -78,8 +78,12 @@ "discriminator", "number", "string", + "color", + "hex", + "hex-color", "random", "seeded-random", + "weighted-random", "prng", "pseudorandom", "deterministic", diff --git a/src/discriminator/discriminator-registry.ts b/src/discriminator/discriminator-registry.ts index eafd781..3a3bf31 100644 --- a/src/discriminator/discriminator-registry.ts +++ b/src/discriminator/discriminator-registry.ts @@ -119,10 +119,35 @@ export class DiscriminatorRegistry { DiscriminatorRegistry.#discriminators.set(registration.discriminator, registration.validator); return (input: unknown): input is T => { - return DiscriminatorRegistry.#validate(input, registration.discriminator); + return DiscriminatorRegistry.validate(input, registration.discriminator); }; } + /** + * Validates an input against a specific discriminator. + * + * @param {unknown} input - The input to validate. + * @param {string} discriminator - The discriminator value to check. + * + * @returns {boolean} - `true` if the input matches the type associated with the discriminator, `false` otherwise. + * + * @public + * @since 0.1.0 + */ + public static validate(input: unknown, discriminator: string): boolean { + if (!DiscriminatorRegistry.#isDiscriminated(input, discriminator)) { + return false; + } + + const validator: ((input: unknown) => boolean) | undefined = DiscriminatorRegistry.#discriminators.get(discriminator); + + if (validator) { + return validator(input); + } + + return false; + } + /** * Validates a discriminator registration object to ensure it has the required properties and that the discriminator value is unique. * @@ -144,45 +169,21 @@ export class DiscriminatorRegistry { throw new TypeError('Registration must be an object.'); } - const registration: DiscriminatorRegistration = input as DiscriminatorRegistration; + const record = input as Record; - if (!StringUtility.isSingleLineTrimmedString(registration.discriminator)) { - throw new TypeError(`Discriminator '${registration.discriminator}' must be a non-empty single line trimmed string.`); + if (!StringUtility.isSingleLineTrimmedString(record['discriminator'])) { + throw new TypeError(`Discriminator '${record['discriminator'] as string}' must be a non-empty single line trimmed string.`); } - if (typeof registration.validator !== 'function') { - throw new TypeError(`Discriminator '${registration.discriminator}' must have a validator function.`); + if (typeof record['validator'] !== 'function') { + throw new TypeError(`Discriminator '${record['discriminator']}' must have a validator function.`); } - if (DiscriminatorRegistry.has(registration.discriminator)) { - throw new Error(`Discriminator '${registration.discriminator}' is already registered.`); + if (DiscriminatorRegistry.has(record['discriminator'])) { + throw new Error(`Discriminator '${record['discriminator']}' is already registered.`); } } - /** - * Validates an input against a specific discriminator. - * - * @param {unknown} input - The input to validate. - * @param {string} discriminator - The discriminator value to check. - * - * @returns {boolean} - `true` if the input matches the type associated with the discriminator, `false` otherwise. - * - * @private - */ - static #validate(input: unknown, discriminator: string): boolean { - if (!DiscriminatorRegistry.#isDiscriminated(input, discriminator)) { - return false; - } - - const validator: ((input: unknown) => boolean) | undefined = DiscriminatorRegistry.#discriminators.get(discriminator); - - if (validator) { - return validator(input); - } - - return false; - } - /** * Is the given input an object with a discriminator property that matches the given discriminator value? * diff --git a/src/discriminator/discriminators.ts b/src/discriminator/discriminators.ts new file mode 100644 index 0000000..870bce7 --- /dev/null +++ b/src/discriminator/discriminators.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024-2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Valid discriminators for package types and interfaces. + * + * @since 0.1.0 + */ +export enum Discriminators { + /** + * Discriminator value for the WeightedElement interface. + * + * @since 0.1.0 + */ + WeightedElement = '@blwatkins/utils:WeightedElement' +} diff --git a/src/discriminator/index.ts b/src/discriminator/index.ts index bbbed3c..0936d44 100644 --- a/src/discriminator/index.ts +++ b/src/discriminator/index.ts @@ -20,3 +20,4 @@ export * from './discriminated'; export * from './discriminator-registry'; +export * from './discriminators'; diff --git a/src/index.ts b/src/index.ts index 2a839d6..8da5a30 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2026 Brittni Watkins. + * Copyright (c) 2022-2026 Brittni Watkins. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), diff --git a/src/number/number-utility.ts b/src/number/number-utility.ts index bf747a2..5b385a2 100644 --- a/src/number/number-utility.ts +++ b/src/number/number-utility.ts @@ -67,18 +67,19 @@ export class NumberUtility { * @param {unknown} input * @param {boolean} zeroInclusive - `true` if zero should be considered a valid input. * `false` if zero should be considered an invalid input. + * Default value is `false`. * * @returns {input is number} `true` if the given input is a positive integer, or zero when `zeroInclusive` is `true`; `false` otherwise. * * @public * @since 0.1.0 */ - public static isPositiveInteger(input: unknown, zeroInclusive?: boolean): input is number { + public static isPositiveInteger(input: unknown, zeroInclusive: boolean = false): input is number { if (!NumberUtility.isInteger(input)) { return false; } - if (zeroInclusive === true) { + if (zeroInclusive) { return input >= 0; } diff --git a/src/random/index.ts b/src/random/index.ts index 97d0f7e..44f402c 100644 --- a/src/random/index.ts +++ b/src/random/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2026 Brittni Watkins. + * Copyright (c) 2022-2026 Brittni Watkins. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), @@ -19,3 +19,6 @@ */ export * from './seeded-random'; +export * from './weighted-element'; + +export * from './random'; diff --git a/src/random/random.ts b/src/random/random.ts new file mode 100644 index 0000000..e2c8df4 --- /dev/null +++ b/src/random/random.ts @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2022-2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { NumberUtility } from '../number'; + +import { WeightedElementUtility, WeightedList } from './weighted-element'; + +/** + * Static properties and methods for generating random numbers and booleans, and for selecting random elements from arrays. + * + * @since 0.1.0 + */ +export class Random { + /** + * The primary function used to generate random numbers. + * By default, this is set to `Math.random`, but it can be overridden for testing or seeded pseudorandom number generation. + * + * @default Math.random + * + * @type {() => number} + * @private + */ + static #rng: () => number = Math.random; + + /** + * @throws {Error} - Random is a static class and cannot be instantiated. + * + * @private + */ + private constructor() { + throw new Error('Random is a static class and cannot be instantiated.'); + } + + /** + * Set the primary function used to generate random numbers. + * + * @param {() => number} rng - A function that returns a random number in the range [0, 1) (zero inclusive, one exclusive). + * + * @returns {void} + * + * @throws {TypeError} - If the given random number generator is not a function. + * + * @public + * @since 0.1.0 + */ + public static set randomNumberGenerator(rng: () => number) { + if (typeof rng !== 'function') { + throw new TypeError('Random number generator must be a function'); + } + + Random.#rng = rng; + } + + /** + * @returns {number} A random number in the range [0, 1) (zero inclusive, one exclusive). + * + * @public + * @since 0.1.0 + */ + public static random(): number { + return Random.#rng(); + } + + /** + * @param {number} min - The minimum value (inclusive). + * @param {number} max - The maximum value (exclusive). + * + * @returns {number} A random floating-point number in the range [min, max) (min inclusive, max exclusive). + * + * @throws {TypeError} When `min` is not a finite number. + * @throws {TypeError} When `max` is not a finite number. + * @throws {RangeError} When `min` is not less than or equal `max`. + * + * @public + * @since 0.1.0 + */ + public static randomFloat(min: number, max: number): number { + Random.#validateRange(min, max); + return (Random.random() * (max - min)) + min; + } + + /** + * If `min` or `max` is not an integer, it is rounded down with `Math.floor` before a value is generated. + * + * @param {number} min - The minimum value (inclusive). + * Non-integer values are rounded down with `Math.floor`. + * @param {number} max - The maximum value (exclusive). + * Non-integer values are rounded down with `Math.floor`. + * + * @returns {number} A random integer in the range [Math.floor(min), Math.floor(max)) (min inclusive, max exclusive). + * + * @throws {TypeError} When `min` is not a finite number. + * @throws {TypeError} When `max` is not a finite number. + * @throws {RangeError} When `min` is not less than or equal `max`. + * + * @public + * @since 0.1.0 + */ + public static randomInt(min: number, max: number): number { + Random.#validateRange(min, max); + const floorMin: number = Math.floor(min); + const floorMax: number = Math.floor(max); + return Math.floor(Random.randomFloat(floorMin, floorMax)); + } + + /** + * If `min` or `max` is not an integer, it is rounded down with `Math.floor` before a value is generated. + * + * @see {@link Random.randomInt} + * + * @param {number} min - The minimum value (inclusive). + * Non-integer values are rounded down with `Math.floor`. + * @param {number} max - The maximum value (exclusive). + * Non-integer values are rounded down with `Math.floor`. + * + * @returns {number} A random integer in the range [Math.floor(min), Math.floor(max)) (min inclusive, max exclusive). + * + * @throws {TypeError} When `min` is not a finite number. + * @throws {TypeError} When `max` is not a finite number. + * @throws {RangeError} When `min` is not less than or equal `max`. + * + * @public + * @since 0.1.0 + */ + public static randomInteger(min: number, max: number): number { + return Random.randomInt(min, max); + } + + /** + * @param {number} chanceOfTrue - The probability of returning `true` (between 0 and 1). + * Default value is `0.5`. + * + * @returns {boolean} A random boolean value. + * + * @public + * @since 0.1.0 + */ + public static randomBoolean(chanceOfTrue: number = 0.5): boolean { + Random.#validateChanceOfTrue(chanceOfTrue); + return Random.random() < chanceOfTrue; + } + + /** + * @param {Type[]} elements - An array of elements to choose from. + * + * @returns {Type} A random element from the array. + * + * @public + * @since 0.1.0 + */ + public static randomElement(elements: Type[]): Type { + Random.#validateElements(elements); + return elements[Random.randomInt(0, elements.length)]; + } + + /** + * @see {@link WeightedElementUtility.isGenericWeightedList} + * @see {@link WeightedElementUtility.isGenericWeightedElement} + * + * @param {WeightedList} elements - The {@link WeightedList} to select a random element from. + * + * @returns {Type} A random element from the given {@link WeightedList}, where the selection probability is equal to the {@link WeightedElement.weight} of each element. + * + * @throws {TypeError} - When the given list is not a valid {@link WeightedList}. + * For a {@link WeightedList} to be valid, it must be a non-empty array of {@link WeightedElement} objects, where the sum of {@link WeightedElement.weight} properties in the array is equal to 1. + * + * @public + * @since 0.1.0 + */ + public static randomWeightedElement(elements: WeightedList): Type { + WeightedElementUtility.validateWeightedList(elements); + const r: number = Random.random(); + let cumulativeWeight: number = 0; + + for (const element of elements) { + cumulativeWeight += element.weight; + if (r < cumulativeWeight) { + return element.value; + } + } + + return elements[elements.length - 1].value; + } + + /** + * Validate min and max values for random number generation. + * + * @see {@link NumberUtility.isFiniteNumber} + * + * @param {unknown} min - Minimum value to validate. Should be a finite number less than or equal to the given max. + * @param {unknown} max - Maximum value to validate. Should be a finite number greater than or equal to the given min. + * + * @throws {TypeError} - When the given min is not a finite number. + * @throws {TypeError} - When the given max is not a finite number. + * @throws {RangeError} - When the given min is not less than or equal to the given max. + * + * @private + */ + static #validateRange(min: unknown, max: unknown): void { + if (!NumberUtility.isFiniteNumber(min)) { + throw new TypeError('min must be a finite number'); + } + + if (!NumberUtility.isFiniteNumber(max)) { + throw new TypeError('max must be a finite number'); + } + + if (min > max) { + throw new RangeError(`min (${min}) must be less than or equal to max (${max})`); + } + } + + /** + * Validate chanceOfTrue input for random boolean generation. + * + * @param {unknown} chanceOfTrue - Chance of returning true. Should be a finite number between 0 and 1 (inclusive). + * + * @throws {TypeError} - When the given chanceOfTrue is not a finite number. + * @throws {RangeError} - When the given chanceOfTrue is not between 0 and 1 (inclusive). + * + * @private + */ + static #validateChanceOfTrue(chanceOfTrue: unknown): void { + if (!NumberUtility.isFiniteNumber(chanceOfTrue)) { + throw new TypeError('chanceOfTrue must be a finite number'); + } + + if (chanceOfTrue < 0 || chanceOfTrue > 1) { + throw new RangeError(`chance (${chanceOfTrue}) must be between 0 and 1`); + } + } + + /** + * Validate elements input for random element selection. + * + * @param {unknown} elements - Elements to select from. Should be a non-empty array. + * + * @throws {TypeError} - When the given elements is not a non-empty array. + * + * @private + */ + static #validateElements(elements: unknown): void { + if (!elements || !Array.isArray(elements) || elements.length === 0) { + throw new TypeError('elements must be a non-empty array'); + } + } +} diff --git a/src/random/seeded-random/random-number-generator-factory.ts b/src/random/seeded-random/random-number-generator-factory.ts index 9000403..2ce6f76 100644 --- a/src/random/seeded-random/random-number-generator-factory.ts +++ b/src/random/seeded-random/random-number-generator-factory.ts @@ -110,11 +110,16 @@ export class RandomNumberGeneratorFactory { } /** + * Validate seed, namespace, and version inputs. + * * @see {@link SeedVersions.isValidIndex} * - * @param {string} seed - seed to validate - * @param {string|undefined} namespace - namespace to validate - * @param {number|undefined} version - version to validate + * @param {unknown} seed - Seed to validate. + * Should be a string. + * @param {unknown} namespace - Namespace to validate. + * Should be undefined or a string. + * @param {unknown} version - Version to validate. + * Should be undefined or an integer that is also a valid {@link SeedVersions} index. * * @throws {TypeError} - When the given seed is not a string. * @throws {TypeError} - When the given namespace is not a string. @@ -123,7 +128,7 @@ export class RandomNumberGeneratorFactory { * * @private */ - static #validateBuildInputs(seed: string, namespace?: string, version?: number): void { + static #validateBuildInputs(seed: unknown, namespace?: unknown, version?: unknown): void { if (!StringUtility.isString(seed)) { throw new TypeError('Seed must be a string.'); } diff --git a/src/random/weighted-element/index.ts b/src/random/weighted-element/index.ts new file mode 100644 index 0000000..cef2c0d --- /dev/null +++ b/src/random/weighted-element/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024-2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +export * from './weighted-element'; +export * from './weighted-element-utility'; diff --git a/src/random/weighted-element/weighted-element-utility.ts b/src/random/weighted-element/weighted-element-utility.ts new file mode 100644 index 0000000..f903226 --- /dev/null +++ b/src/random/weighted-element/weighted-element-utility.ts @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import Value from 'typebox/value'; + +import { Type } from 'typebox'; + +import { DiscriminatorRegistry, Discriminators, TypeGuard } from '../../discriminator'; + +import { WeightedElement, WeightedList, weightedElementSchema } from './weighted-element'; + +/** + * Static methods and properties for building and validating {@link WeightedElement} and {@link WeightedList} objects. + * + * @since 0.1.0 + */ +export class WeightedElementUtility { + /** + * @throws {Error} - WeightedElementUtility is a static class and cannot be instantiated. + * + * @private + */ + private constructor() { + throw new Error('WeightedElementUtility is a static class and cannot be instantiated.'); + } + + /** + * A type guard for {@link WeightedElement} objects. + * + * @param {unknown} input - The input to check. + * + * @returns {boolean} `true` if the input is a {@link WeightedElement}, `false` otherwise. + * + * @readonly + * @private + */ + static readonly #isGenericWeightedElement: TypeGuard> = DiscriminatorRegistry.register>({ + discriminator: Discriminators.WeightedElement, + validator: (input: unknown): input is WeightedElement => { + return Value.Check(Type.Call(weightedElementSchema, [Type.Unknown()]), input); + } + }); + + /** + * Builds a {@link WeightedElement} object with a value of the given type. + * + * @see {@link WeightedElementUtility.isGenericWeightedElement} + * + * @param {{ value: TValue; weight: number; }} input - The input to build the {@link WeightedElement} from. + * + * @returns {WeightedElement} A {@link WeightedElement} object with a value of the given type. + * + * @throws {TypeError} - When the given input is not an object. + * @throws {TypeError} - When the given input does not result in a valid {@link WeightedElement}. + * See {@link weightedElementSchema} for the requirements of a valid {@link WeightedElement}. + * + * @public + * @since 0.1.0 + */ + public static buildWeightedElement(input: { value: TValue; weight: number; }): WeightedElement { + WeightedElementUtility.#validateBuildWeightedElementInput(input); + + const weightedElement = { + ...input, + discriminator: Discriminators.WeightedElement + }; + + WeightedElementUtility.#validateWeightedElement(weightedElement); + return weightedElement; + } + + /** + * Builds a {@link WeightedList} object from the given elements list. + * + * @param {{ value: TValue; weight: number }[]} elements - The elements to build the {@link WeightedList} from. + * Each element will be converted into a {@link WeightedElement} using {@link WeightedElementUtility.buildWeightedElement}. + * + * @returns {WeightedList} A {@link WeightedList} object containing the given elements. + * + * @throws {TypeError} - When the given elements are not a non-empty array. + * @throws {TypeError} - When the given elements do not result in a valid {@link WeightedList}. + * + * @public + * @since 0.1.0 + */ + public static buildWeightedList(elements: { value: TValue; weight: number; }[]): WeightedList { + WeightedElementUtility.#validateBuildWeightedListInput(elements); + + const weightedElements: WeightedElement[] = elements.map((element: { value: TValue; weight: number; }): WeightedElement => { + return WeightedElementUtility.buildWeightedElement(element); + }); + + WeightedElementUtility.validateWeightedList(weightedElements); + return weightedElements; + } + + /** + * Is the given input a {@link WeightedElement} object? + * This method does not enforce type checking for {@link WeightedElement.value}. + * + * @param {unknown} input - The input to check. + * + * @returns {input is WeightedElement} - `true` if the given input is a {@link WeightedElement} object; `false` otherwise. + * + * @public + * @since 0.1.0 + */ + public static isGenericWeightedElement(input: unknown): input is WeightedElement { + return WeightedElementUtility.#isGenericWeightedElement(input); + } + + /** + * Is the given input a {@link WeightedElement} object, whose {@link WeightedElement.value} property passes the given type guard function? + * + * @param {unknown} input - The input to check. + * @param {(value: unknown) => boolean} valueTypeGuard - The method used to validate the type of {@link WeightedElement.value}. + * This method should return `true` if the value is of the expected type, and `false` otherwise. + * The type validated by the function should match the assigned type of the {@link WeightedElement}. + * + * @returns {input is WeightedElement} - `true` if the given input is a {@link WeightedElement} object with a value of the correct type; `false` otherwise. + * + * @throws {TypeError} - When the given `valueTypeGuard` is not a function. + * + * @public + * @since 0.1.0 + */ + public static isWeightedElement(input: unknown, valueTypeGuard: (value: unknown) => value is TValue): input is WeightedElement { + if (typeof valueTypeGuard !== 'function') { + throw new TypeError('Value type guard must be a function'); + } + + return WeightedElementUtility.isGenericWeightedElement(input) && valueTypeGuard(input.value); + } + + /** + * Is the given input a {@link WeightedList} object? + * This method does not enforce type checking for the {@link WeightedElement.value} property of the list elements. + * + * @see {@link WeightedElementUtility.isGenericWeightedElement} + * + * @param {unknown} input - The input to check. + * + * @returns {input is WeightedList} - `true` if the given input is a valid {@link WeightedList} object; `false` otherwise. + * For a {@link WeightedList} to be valid, it must be a non-empty array of {@link WeightedElement} objects, where the sum of {@link WeightedElement.weight} properties in the array is equal to 1. + * + * @public + * @since 0.1.0 + */ + public static isGenericWeightedList(input: unknown): input is WeightedList { + if (!input || !Array.isArray(input) || input.length === 0) { + return false; + } + + const containsWeightedElements: boolean = input.every((element: unknown): boolean => { + return WeightedElementUtility.isGenericWeightedElement(element); + }); + + if (!containsWeightedElements) { + return false; + } + + const weightSum: number = input.reduce((sum: number, element: unknown): number => sum + (element as WeightedElement).weight, 0); + const precisionSum: number = Number.parseFloat(weightSum.toFixed(4)); + return precisionSum === 1; + } + + /** + * Is the given input a {@link WeightedList} object, where each {@link WeightedElement} object in the array contains a {@link WeightedElement.value} property that passes the given type guard function? + * + * @see {@link WeightedElementUtility.isGenericWeightedList} + * + * @param {unknown} input - The input to check. + * @param {(value: unknown) => boolean} valueTypeGuard - The method used to validate the type of each {@link WeightedElement.value} in the array. + * This method should return `true` if the value is of the expected type, and `false` otherwise. + * The type validated by the function should match the assigned type of the {@link WeightedList}. + * + * @returns {input is WeightedList} - `true` if the given input is a {@link WeightedList} object with elements of the correct type; `false` otherwise. + * For a {@link WeightedList} to be valid, it must be a non-empty array of {@link WeightedElement} objects, where the sum of {@link WeightedElement.weight} properties in the array is equal to 1. + * + * @throws {TypeError} - When the given `valueTypeGuard` is not a function. + * + * @public + * @since 0.1.0 + */ + public static isWeightedList(input: unknown, valueTypeGuard: (value: unknown) => value is TValue): input is WeightedList { + if (typeof valueTypeGuard !== 'function') { + throw new TypeError('Value type guard must be a function'); + } + + if (!WeightedElementUtility.isGenericWeightedList(input)) { + return false; + } + + return input.every((element: unknown): boolean => { + return WeightedElementUtility.isWeightedElement(element, valueTypeGuard); + }); + } + + /** + * Validate that an object is a valid {@link WeightedList}. + * This method does not enforce type checking for the {@link WeightedElement.value} property of the given elements in the list. + * + * @see {@link WeightedElementUtility.isGenericWeightedList} + * + * @param {unknown} list - The list to validate. + * + * @returns {void} + * + * @throws {TypeError} - When the given list is not a valid {@link WeightedList}. + * + * @public + * @since 0.1.0 + */ + public static validateWeightedList(list: unknown): void { + if (!WeightedElementUtility.isGenericWeightedList(list)) { + throw new TypeError('Input does not match schema requirements for weighted list'); + } + } + + /** + * Validate the input of {@link WeightedElementUtility.buildWeightedElement}. + * + * @param {unknown} input - The input to validate. + * + * @returns {void} + * + * @throws {TypeError} - When the given input is not an object. + * + * @private + */ + static #validateBuildWeightedElementInput(input: unknown): void { + if (!input || typeof input !== 'object' || Array.isArray(input)) { + throw new TypeError('Input must be an object'); + } + } + + /** + * Validate the input of {@link WeightedElementUtility.buildWeightedList}. + * + * @param {unknown} input - The input to validate. + * + * @returns {void} + * + * @throws {TypeError} - When the given input is not a non-empty array. + * + * @private + */ + static #validateBuildWeightedListInput(input: unknown): void { + if (!input || !Array.isArray(input) || input.length === 0) { + throw new TypeError('Input must be a non-empty array'); + } + } + + /** + * Validate that an object is a valid {@link WeightedElement}. + * This method does not enforce type checking for the {@link WeightedElement.value} property of the given element. + * + * @see {@link WeightedElementUtility.isGenericWeightedElement} + * + * @param {unknown} element - The element to validate. + * + * @returns {void} + * + * @throws {TypeError} - When the given element is not a valid {@link WeightedElement}. + * + * @private + */ + static #validateWeightedElement(element: unknown): void { + if (!WeightedElementUtility.isGenericWeightedElement(element)) { + throw new TypeError(`Element does not match schema requirements for weighted element`); + } + } +} diff --git a/src/random/weighted-element/weighted-element.ts b/src/random/weighted-element/weighted-element.ts new file mode 100644 index 0000000..9d365f4 --- /dev/null +++ b/src/random/weighted-element/weighted-element.ts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024-2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { Type } from 'typebox'; + +import { Discriminators, discriminatedSchema } from '../../discriminator'; + +/** + * TypeBox schema to validate a {@link WeightedElement} object. + * + * @since 0.1.0 + */ +export const weightedElementSchema = Type.Generic( + [Type.Parameter('T')], + Type.Intersect([ + discriminatedSchema, + Type.Object( + { + /** + * The value to be selected from the weighted list. + * + * @readonly + */ + value: Type.Readonly(Type.Ref('T')), + + /** + * The probability weight of the element. + * Should be a number between 0 and 1, inclusive. + * + * @readonly + * @type {number} + */ + weight: Type.Readonly(Type.Number({ + minimum: 0, + maximum: 1 + })), + + /** + * The discriminator for the weighted element. + * + * @readonly + * @type {Discriminators.WeightedElement} + */ + discriminator: Type.Literal(Discriminators.WeightedElement) + }, + { additionalProperties: false } + ) + ]) +); + +/** + * Interface for a weighted element, which can be used for non-uniform random selection from a list. + * + * @since 0.1.0 + */ +export interface WeightedElement { + /** + * The value to be selected from the weighted list. + * + * @readonly + * @since 0.1.0 + */ + readonly value: TValue; + + /** + * The probability weight of the element. + * Should be a number between 0 and 1, inclusive. + * + * @readonly + * @since 0.1.0 + * @type {number} + */ + readonly weight: number; + + /** + * The discriminator for the weighted element. + * + * @readonly + * @since 0.1.0 + * @type {Discriminators.WeightedElement} + */ + readonly discriminator: Discriminators.WeightedElement; +} + +/** + * Type alias for a list of {@link WeightedElement} objects. + * + * @see {@link weightedElementSchema} + * + * @since 0.1.0 + */ +export type WeightedList = WeightedElement[]; diff --git a/src/string/color-string-utility.ts b/src/string/color-string-utility.ts new file mode 100644 index 0000000..48604e6 --- /dev/null +++ b/src/string/color-string-utility.ts @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024-2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { StringUtility } from './string-utility'; + +const regularExpressions = { + hexColor: /^(#[A-F0-9]{6}(?:[A-F0-9]{2})?|#[a-f0-9]{6}(?:[a-f0-9]{2})?)$/, + hexColorRGB: /^(#[A-F0-9]{6}|#[a-f0-9]{6})$/, + hexColorRGBA: /^(#[A-F0-9]{8}|#[a-f0-9]{8})$/ +}; + +/** + * Static properties and methods for validating formatted color strings. + * + * @since 0.1.0 + */ +export class ColorStringUtility { + /** + * @throws {Error} - ColorStringUtility is a static class and cannot be instantiated. + * + * @private + */ + private constructor() { + throw new Error('ColorStringUtility is a static class and cannot be instantiated.'); + } + + /** + * @returns {RegExp} Regular expression pattern for validating hex color strings in the format `#RRGGBB` or `#RRGGBBAA`. + * Case must be consistent in hex color strings: either all lowercase or all uppercase. + * + * @public + * @since 0.1.0 + */ + public static get hexColorPattern(): RegExp { + return regularExpressions.hexColor; + } + + /** + * @returns {RegExp} Regular expression pattern for validating hex color strings in the format `#RRGGBB`. + * Case must be consistent in hex color strings: either all lowercase or all uppercase. + * + * @public + * @since 0.1.0 + */ + public static get hexColorPatternRGB(): RegExp { + return regularExpressions.hexColorRGB; + } + + /** + * @returns {RegExp} Regular expression pattern for validating hex color strings in the format `#RRGGBBAA`. + * Case must be consistent in hex color strings: either all lowercase or all uppercase. + * + * @public + * @since 0.1.0 + */ + public static get hexColorPatternRGBA(): RegExp { + return regularExpressions.hexColorRGBA; + } + + /** + * Is the given input a string matching the {@link ColorStringUtility.hexColorPattern} pattern? + * + * @param {unknown} input + * + * @returns {input is string} `true` if the given input matches the {@link ColorStringUtility.hexColorPattern} pattern; `false` otherwise. + * + * @public + * @since 0.1.0 + */ + public static isHexColor(input: unknown): input is string { + return StringUtility.isString(input) && ColorStringUtility.hexColorPattern.test(input); + } + + /** + * Is the given input a string matching the {@link ColorStringUtility.hexColorPatternRGB} pattern? + * + * @param {unknown} input + * + * @returns {input is string} `true` if the given input matches the {@link ColorStringUtility.hexColorPatternRGB} pattern; `false` otherwise. + * + * @public + * @since 0.1.0 + */ + public static isHexColorRGB(input: unknown): input is string { + return StringUtility.isString(input) && ColorStringUtility.hexColorPatternRGB.test(input); + } + + /** + * Is the given input a string matching the {@link ColorStringUtility.hexColorPatternRGBA} pattern? + * + * @param {unknown} input + * + * @returns {input is string} `true` if the given input matches the {@link ColorStringUtility.hexColorPatternRGBA} pattern; `false` otherwise. + * + * @public + * @since 0.1.0 + */ + public static isHexColorRGBA(input: unknown): input is string { + return StringUtility.isString(input) && ColorStringUtility.hexColorPatternRGBA.test(input); + } +} diff --git a/src/string/index.ts b/src/string/index.ts index 2420d3f..1d4b618 100644 --- a/src/string/index.ts +++ b/src/string/index.ts @@ -18,4 +18,5 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +export * from './color-string-utility'; export * from './string-utility'; diff --git a/src/string/string-utility.ts b/src/string/string-utility.ts index bb77977..33e91fe 100644 --- a/src/string/string-utility.ts +++ b/src/string/string-utility.ts @@ -18,7 +18,7 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -const RegularExpressions = { +const regularExpressions = { singleLineLowercaseTrimmed: /^(?!\s)(?!.*\s$)(?!.*\p{Lu})(?!.* {2})[^\t\r\n]+$/u, singleLineUppercaseTrimmed: /^(?!\s)(?!.*\s$)(?!.*\p{Ll})(?!.* {2})[^\t\r\n]+$/u, singleLineTrimmed: /^(?!\s)(?!.*\s$)(?!.* {2})[^\t\r\n]+$/ @@ -48,7 +48,7 @@ export class StringUtility { * @since 0.1.0 */ public static get singleLineLowercaseTrimmedPattern(): RegExp { - return RegularExpressions.singleLineLowercaseTrimmed; + return regularExpressions.singleLineLowercaseTrimmed; } /** @@ -60,7 +60,7 @@ export class StringUtility { * @since 0.1.0 */ public static get singleLineUppercaseTrimmedPattern(): RegExp { - return RegularExpressions.singleLineUppercaseTrimmed; + return regularExpressions.singleLineUppercaseTrimmed; } /** @@ -72,7 +72,7 @@ export class StringUtility { * @since 0.1.0 */ public static get singleLineTrimmedPattern(): RegExp { - return RegularExpressions.singleLineTrimmed; + return regularExpressions.singleLineTrimmed; } /** @@ -95,12 +95,12 @@ export class StringUtility { * * @param {unknown} input * - * @returns {boolean} + * @returns {input is string} * * @public * @since 0.1.0 */ - public static isNonEmptyString(input: unknown): boolean { + public static isNonEmptyString(input: unknown): input is string { return StringUtility.isString(input) && (input.trim().length > 0); } @@ -111,13 +111,13 @@ export class StringUtility { * * @param {unknown} input * - * @returns {boolean} + * @returns {input is string} * * @public * @since 0.1.0 */ - public static isSingleLineLowercaseTrimmedString(input: unknown): boolean { - return StringUtility.isString(input) && (StringUtility.singleLineLowercaseTrimmedPattern.test(input)); + public static isSingleLineLowercaseTrimmedString(input: unknown): input is string { + return StringUtility.isString(input) && StringUtility.singleLineLowercaseTrimmedPattern.test(input); } /** @@ -127,13 +127,13 @@ export class StringUtility { * * @param {unknown} input * - * @returns {boolean} + * @returns {input is string} * * @public * @since 0.1.0 */ - public static isSingleLineUppercaseTrimmedString(input: unknown): boolean { - return StringUtility.isString(input) && (StringUtility.singleLineUppercaseTrimmedPattern.test(input)); + public static isSingleLineUppercaseTrimmedString(input: unknown): input is string { + return StringUtility.isString(input) && StringUtility.singleLineUppercaseTrimmedPattern.test(input); } /** @@ -143,12 +143,12 @@ export class StringUtility { * * @param {unknown} input * - * @returns {boolean} + * @returns {input is string} * * @public * @since 0.1.0 */ - public static isSingleLineTrimmedString(input: unknown): boolean { - return StringUtility.isString(input) && (StringUtility.singleLineTrimmedPattern.test(input)); + public static isSingleLineTrimmedString(input: unknown): input is string { + return StringUtility.isString(input) && StringUtility.singleLineTrimmedPattern.test(input); } } diff --git a/test/discriminator/discriminator-registry.test.ts b/test/discriminator/discriminator-registry.test.ts index 2e20473..8b48a21 100644 --- a/test/discriminator/discriminator-registry.test.ts +++ b/test/discriminator/discriminator-registry.test.ts @@ -65,10 +65,10 @@ describe('DiscriminatorRegistry', (): void => { expect(DiscriminatorRegistry.has(TestDiscriminators.TEST)).toBe(true); }); - describe('input validation', (): void => { + describe('Input validation', (): void => { const scenarios: Scenario[] = [ { - label: 'non-string discriminators', + label: 'Non-string discriminators', inputs: [ ...nonStringInputs ], @@ -83,7 +83,7 @@ describe('DiscriminatorRegistry', (): void => { test.each( testCases - )('%# - input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect(DiscriminatorRegistry.has(testInput as string)).toBe(testExpected); }); }); @@ -95,17 +95,17 @@ describe('DiscriminatorRegistry', (): void => { }); describe('register', (): void => { - describe('Register should return a method that validates the registered Discriminated type', (): void => { + describe('register() should return a method that validates the registered Discriminated type', (): void => { const scenarios: Scenario[] = [ { - label: 'non-object inputs', + label: 'Non-object inputs', inputs: [ ...nonObjectInputs ], expected: false }, { - label: 'array inputs', + label: 'Array inputs', inputs: [ [], ['value'], @@ -114,7 +114,7 @@ describe('DiscriminatorRegistry', (): void => { expected: false }, { - label: 'object inputs without discriminator', + label: 'Object inputs without discriminator', inputs: [ {}, { property: 'value' }, @@ -123,7 +123,7 @@ describe('DiscriminatorRegistry', (): void => { expected: false }, { - label: 'object inputs with incorrect discriminator', + label: 'Object inputs with incorrect discriminator', inputs: [ { discriminator: '' }, { discriminator: 'invalid' }, @@ -136,7 +136,7 @@ describe('DiscriminatorRegistry', (): void => { expected: false }, { - label: 'object inputs with correct discriminator, but incorrect schema', + label: 'Object inputs with correct discriminator, but incorrect schema', inputs: [ { discriminator: TestDiscriminators.TEST @@ -161,7 +161,7 @@ describe('DiscriminatorRegistry', (): void => { expected: false }, { - label: 'object inputs with correct schema', + label: 'Object inputs with correct schema', inputs: [ { discriminator: TestDiscriminators.TEST, @@ -183,7 +183,7 @@ describe('DiscriminatorRegistry', (): void => { test.each( testCases - )('%# - input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect(isTestObject(testInput)).toBe(testExpected); }); }); @@ -233,14 +233,14 @@ describe('DiscriminatorRegistry', (): void => { const scenarios: Scenario[] = [ { - label: 'non-object registration', + label: 'Non-object registration', inputs: [ ...nonObjectInputs ], expected: TypeError }, { - label: 'array registration', + label: 'Array registration', inputs: [ [], ['value'], @@ -249,7 +249,7 @@ describe('DiscriminatorRegistry', (): void => { expected: TypeError }, { - label: 'non-string discriminators', + label: 'Non-string discriminators', inputs: [ ...buildRegistrations([ ...nonStringInputs @@ -258,7 +258,7 @@ describe('DiscriminatorRegistry', (): void => { expected: TypeError }, { - label: 'empty string discriminators', + label: 'Empty string discriminators', inputs: [ ...buildRegistrations([ ...emptyStringInputs @@ -267,7 +267,7 @@ describe('DiscriminatorRegistry', (): void => { expected: TypeError }, { - label: 'multi-line string discriminators', + label: 'Multi-line string discriminators', inputs: [ ...buildRegistrations([ ...singleLineTrimmedFailureInputs @@ -276,7 +276,7 @@ describe('DiscriminatorRegistry', (): void => { expected: TypeError }, { - label: 'non-function validators', + label: 'Non-function validators', inputs: [ ...buildRegistrations(['test-discriminator'], [ ...nonFunctionInputs @@ -285,7 +285,7 @@ describe('DiscriminatorRegistry', (): void => { expected: TypeError }, { - label: 'duplicate discriminator registration', + label: 'Duplicate discriminator registration', inputs: [ ...buildRegistrations([TestDiscriminators.TEST], [(): boolean => false]) ], @@ -300,10 +300,20 @@ describe('DiscriminatorRegistry', (): void => { test.each( testCases - )('%# - invalid registration $input should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Invalid registration $input should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect((): TypeGuard => DiscriminatorRegistry.register(testInput as DiscriminatorRegistration)).toThrow(testExpected); }); }); }); }); + + describe('validate', (): void => { + test('validate() returns false for unregistered discriminator', (): void => { + const input = { + discriminator: 'unregistered' + }; + + expect(DiscriminatorRegistry.validate(input, 'unregistered')).toBe(false); + }); + }); }); diff --git a/test/discriminator/discriminators.test.ts b/test/discriminator/discriminators.test.ts new file mode 100644 index 0000000..4905074 --- /dev/null +++ b/test/discriminator/discriminators.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { describe, test, expect } from 'vitest'; + +import { Discriminators } from '../../src'; + +describe('Discriminators', (): void => { + test('All discriminators should be unique', (): void => { + const discriminatorsArray: Discriminators[] = Array.from(Object.values(Discriminators)); + const uniqueDiscriminators = new Set(discriminatorsArray); + expect(uniqueDiscriminators.size).toBe(discriminatorsArray.length); + }); +}); diff --git a/test/number/number-utility.test.ts b/test/number/number-utility.test.ts index 293815a..c6cc9e3 100644 --- a/test/number/number-utility.test.ts +++ b/test/number/number-utility.test.ts @@ -38,12 +38,12 @@ describe('NumberUtility', (): void => { describe('isFiniteNumber', (): void => { const scenarios: Scenario[] = [ { - label: 'non-number inputs', + label: 'Non-number inputs', inputs: [...nonNumberInputs], expected: false }, { - label: 'number inputs', + label: 'Number inputs', inputs: [ ...positiveNumberInputs, ...negativeNumberInputs, @@ -73,7 +73,7 @@ describe('NumberUtility', (): void => { test.each( testCases - )('%# - $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect(NumberUtility.isFiniteNumber(testInput)).toBe(testExpected); }); }); diff --git a/test/random/random.test.ts b/test/random/random.test.ts new file mode 100644 index 0000000..cb8f492 --- /dev/null +++ b/test/random/random.test.ts @@ -0,0 +1,981 @@ +/* + * Copyright (c) 2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { describe, test, afterEach, expect, expectTypeOf } from 'vitest'; + +import { + Random, + RandomNumberGeneratorFactory, + SeededRandomNumberGenerator, + WeightedElementUtility, + WeightedList +} from '../../src'; + +import { nonFunctionInputs } from '../utils/input/function-inputs'; +import { nonFiniteNumberInputs, nonNumberInputs } from '../utils/input/number-inputs'; +import { buildTestCases, Scenario, TestCase } from '../utils/test-case/test-case'; + +import { + asciiNamespace, + asciiSeed, getExpectedAsyncSequence, + getExpectedSequence +} from '../utils/test-case/scenarios/random-number-generator-factory-scenarios'; +import { nonArrayInputs } from '../utils/input/array-inputs'; + +describe('Random', (): void => { + const testRepeatTotal: number = 50; + + afterEach((): void => { + Random.randomNumberGenerator = Math.random; + }); + + function validateRandomFloatValues(numbers: number[], min: number, max: number): void { + const sameMinMax: boolean = min !== max; + + for (const num of numbers) { + expectTypeOf(num).toBeNumber(); + expect(num).not.toBeNaN(); + + if (sameMinMax) { + expect(num).toBeGreaterThanOrEqual(min); + expect(num).toBeLessThan(max); + } else { + expect(num).toBe(min); + } + } + + const numbersSet: Set = new Set(numbers); + + if (sameMinMax) { + expect(numbersSet.size).toBe(numbers.length); + } else { + expect(numbersSet.size).toBe(1); + } + } + + function validateRandomIntValues(numbers: number[], min: number, max: number): void { + const sameMinMax: boolean = Math.floor(max) - Math.floor(min) > 1; + + for (const num of numbers) { + expectTypeOf(num).toBeNumber(); + expect(num).not.toBeNaN(); + expect(Number.isInteger(num)).toBe(true); + + if (sameMinMax) { + expect(num).toBeGreaterThanOrEqual(Math.floor(min)); + expect(num).toBeLessThan(Math.floor(max)); + } else { + expect(num).toBe(Math.floor(min)); + } + } + + const numbersSet: Set = new Set(numbers); + + if (sameMinMax) { + expect(numbersSet.size).toBeGreaterThan(1); + expect(numbersSet.size).toBeLessThanOrEqual(Math.floor(max) - Math.floor(min)); + } else { + expect(numbersSet.size).toBe(1); + } + } + + function validateRandomBooleans(booleans: boolean[], expectedValue?: boolean): void { + for (const bool of booleans) { + expectTypeOf(bool).toBeBoolean(); + + if (expectedValue === undefined) { + expect(bool).toBeOneOf([true, false]); + } else if (expectedValue) { + expect(bool).toBe(true); + } else { + expect(bool).toBe(false); + } + } + + const booleanSet: Set = new Set(booleans); + + if (expectedValue === undefined) { + expect(booleanSet.size).toBe(2); + } else { + expect(booleanSet.size).toBe(1); + } + } + + function validateRandomElements(selected: unknown[], input: unknown[], type: string): void { + for (const element of selected) { + expect(typeof element).toBe(type); + expect(element).toBeOneOf(input); + } + + const elementSet: Set = new Set(selected); + expect(elementSet.size).toBe(input.length); + } + + describe('new Random()', (): void => { + describe('Runtime behavior guards', (): void => { + test('Constructor should throw an error when instantiated at runtime', (): void => { + const RuntimeConstructor = Random as unknown as new () => Random; + expect((): Random => new RuntimeConstructor()).toThrow(Error); + }); + }); + }); + + describe('randomNumberGenerator', (): void => { + describe('Setting random number generator should impact the values returned by all other methods', (): void => { + test('random', (): void => { + const expected: number = 1.5; + + Random.randomNumberGenerator = (): number => { + return expected; + }; + + for (let i: number = 0; i < testRepeatTotal; i++) { + expect(Random.random()).toBe(expected); + } + }); + + test('randomFloat', (): void => { + const expected: number = 2; + + Random.randomNumberGenerator = (): number => { + return expected; + }; + + for (let i: number = 0; i < testRepeatTotal; i++) { + expect(Random.randomFloat(0, 1)).toBe(expected); + } + }); + + test('randomInt', (): void => { + const random: number = 2.5; + const expected: number = Math.floor(random); + + Random.randomNumberGenerator = (): number => { + return random; + }; + + for (let i: number = 0; i < testRepeatTotal; i++) { + expect(Random.randomInt(0, 1)).toBe(expected); + } + }); + + test('randomInteger', (): void => { + const random: number = 3.5; + const expected: number = Math.floor(random); + + Random.randomNumberGenerator = (): number => { + return random; + }; + + for (let i: number = 0; i < testRepeatTotal; i++) { + expect(Random.randomInteger(0, 1)).toBe(expected); + } + }); + }); + + describe('Setting random number generator with a seeded pseudorandom number generator should return the correct sequence of values', (): void => { + const seed: string = asciiSeed; + const namespace: string = asciiNamespace; + + test('With RandomNumberGeneratorFactory.build', (): void => { + const rng: SeededRandomNumberGenerator = RandomNumberGeneratorFactory.build(seed, namespace); + const expected: number[] = getExpectedSequence(seed, namespace); + const repeatTotal: number = expected.length; + + Random.randomNumberGenerator = rng.next.bind(rng); + const selected: number[] = []; + + for (let i: number = 0; i < repeatTotal; i++) { + selected.push(Random.random()); + } + + expect(selected).toEqual(expected); + }); + + test('With RandomNumberGeneratorFactory.asyncBuild', async (): Promise => { + const rng: SeededRandomNumberGenerator = await RandomNumberGeneratorFactory.asyncBuild(seed, namespace); + const expected: number[] = getExpectedAsyncSequence(seed, namespace); + const repeatTotal: number = expected.length; + + Random.randomNumberGenerator = rng.next.bind(rng); + const selected: number[] = []; + + for (let i: number = 0; i < repeatTotal; i++) { + selected.push(Random.random()); + } + + expect(selected).toEqual(expected); + }); + }); + + describe('Input validation', (): void => { + describe('randomNumberGenerator must be a function', (): void => { + test.each( + nonFunctionInputs + )('%# - Random.randomNumberGenerator = %o should throw a TypeError', (input: unknown): void => { + expect((): void => { + Random.randomNumberGenerator = input as (() => number); + }).toThrow(TypeError); + }); + }); + }); + }); + + describe('random', (): void => { + test('random() should return a positive number between 0 inclusive and 1 exclusive', (): void => { + const min: 0 = 0 as const; + const max: 1 = 1 as const; + const numbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + const r: number = Random.random(); + numbers.push(r); + } + + validateRandomFloatValues(numbers, min, max); + }); + }); + + describe('randomFloat', (): void => { + describe('randomFloat should return a number between the given min and max', (): void => { + test.each([ + { min: 0, max: 1 }, + { min: 0, max: 50 }, + { min: 100, max: 500 }, + { min: -1, max: 0 }, + { min: -50, max: 0 }, + { min: -500, max: -100 }, + { min: 0.5, max: 0.75 }, + { min: 0, max: 0.5 }, + { min: -0.5, max: 0 }, + { min: -0.75, max: -0.5 }, + { min: -100, max: 100 }, + { min: -0.5, max: 0.5 } + ])('%# - randomFloat($min, $max) should return a number between $min and $max', ({ min, max }: { min: number; max: number; }): void => { + const numbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + const r: number = Random.randomFloat(min, max); + numbers.push(r); + } + + validateRandomFloatValues(numbers, min, max); + }); + }); + + describe('randomFloat should return min when min and max are equal', (): void => { + test.each([ + { min: 0, max: 0 }, + { min: 1, max: 1 }, + { min: 10, max: 10 }, + { min: -10, max: -10 }, + { min: 0.5, max: 0.5 }, + { min: -0.5, max: -0.5 } + ])('%# - randomFloat($min, $max) should return $min', ({ min, max }: { min: number; max: number; }): void => { + const numbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + const r: number = Random.randomFloat(min, max); + numbers.push(r); + } + + validateRandomFloatValues(numbers, min, max); + }); + }); + }); + + describe('randomInt and randomInteger', (): void => { + describe('randomInt and randomInteger should return a number between the given min and max', (): void => { + test.each([ + { min: 0, max: 1 }, + { min: 0, max: 50 }, + { min: 100, max: 500 }, + { min: -1, max: 0 }, + { min: -50, max: 0 }, + { min: -500, max: -100 }, + { min: -100, max: 100 } + ])('%# - randomInt($min, $max) and randomInteger($min, $max) should return a number between $min and $max', ({ min, max }: { min: number; max: number; }): void => { + const intNumbers: number[] = []; + const integerNumbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + intNumbers.push(Random.randomInt(min, max)); + integerNumbers.push(Random.randomInteger(min, max)); + } + + validateRandomIntValues(intNumbers, min, max); + validateRandomIntValues(integerNumbers, min, max); + }); + }); + + describe('randomInt and randomInteger should return a number between Math.floor(min) and Math.floor(max) when the given min and max are float number types', (): void => { + test.each([ + { min: 0.33, max: 1.5 }, + { min: 0.5, max: 50.34 }, + { min: 125.555, max: 400.444 }, + { min: -1.6, max: 0.7 }, + { min: -50.41, max: 0.78 }, + { min: -500.234, max: -100.987 }, + { min: 0.5, max: 10.314 }, + { min: 1.5, max: 9.75 }, + { min: 0, max: 5.5 }, + { min: -0.5, max: 0 }, + { min: -3.75, max: -0.5 }, + { min: -100.777, max: 100.222 }, + { min: -0.5, max: 0.5 } + ])('%# - randomInt($min, $max) and randomInteger($min, $max) should return a number between Math.floor($min) and Math.floor($max)', ({ min, max }: { min: number; max: number; }): void => { + const intNumbers: number[] = []; + const integerNumbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + intNumbers.push(Random.randomInt(min, max)); + integerNumbers.push(Random.randomInteger(min, max)); + } + + validateRandomIntValues(intNumbers, min, max); + validateRandomIntValues(integerNumbers, min, max); + }); + }); + + describe('randomInt and randomInteger should return min when min and max are equal', (): void => { + test.each([ + { min: 0, max: 0 }, + { min: 1, max: 1 }, + { min: -1, max: -1 }, + { min: 10, max: 10 }, + { min: -10, max: -10 } + ])('%# - randomInt($min, $max) and randomInteger($min, $max) should return $min', ({ min, max }: { min: number; max: number; }): void => { + const intNumbers: number[] = []; + const integerNumbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + intNumbers.push(Random.randomInt(min, max)); + integerNumbers.push(Random.randomInteger(min, max)); + } + + validateRandomIntValues(intNumbers, min, max); + validateRandomIntValues(integerNumbers, min, max); + }); + }); + + describe('randomInt and randomInteger should return Math.floor(min) when min and max are equal and float number types', (): void => { + test.each([ + { min: 1.8, max: 1.8 }, + { min: 10.5, max: 10.5 }, + { min: -10.5, max: -10.5 }, + { min: 0.5, max: 0.5 }, + { min: -0.5, max: -0.5 } + ])('%# - randomInt($min, $max) and randomInteger($min, $max) should return Math.floor($min)', ({ min, max }: { min: number; max: number; }): void => { + const intNumbers: number[] = []; + const integerNumbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + intNumbers.push(Random.randomInt(min, max)); + integerNumbers.push(Random.randomInteger(min, max)); + } + + validateRandomIntValues(intNumbers, min, max); + validateRandomIntValues(integerNumbers, min, max); + }); + }); + + describe('randomInt and randomInteger should return Math.floor(min) when Math.floor(min) and Math.floor(max) are equal', (): void => { + test.each([ + { min: 1.5, max: 1.89 }, + { min: 10.01, max: 10.99 }, + { min: 10.001, max: 10.999 }, + { min: -10.4, max: -10.25 }, + { min: 0.5, max: 0.75 }, + { min: 0.99, max: 0.999 }, + { min: -0.999, max: -0.99 } + ])('%# - randomInt($min, $max) and randomInteger($min, $max) should return Math.floor($min)', ({ min, max }: { min: number; max: number; }): void => { + const intNumbers: number[] = []; + const integerNumbers: number[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + intNumbers.push(Random.randomInt(min, max)); + integerNumbers.push(Random.randomInteger(min, max)); + } + + validateRandomIntValues(intNumbers, min, max); + validateRandomIntValues(integerNumbers, min, max); + }); + }); + }); + + describe('randomBoolean', (): void => { + test('randomBoolean should only return true or false', (): void => { + const booleans: boolean[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + booleans.push(Random.randomBoolean()); + } + + validateRandomBooleans(booleans); + }); + + test('randomBoolean(0) should always return false', (): void => { + const booleans: boolean[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + booleans.push(Random.randomBoolean(0)); + } + + validateRandomBooleans(booleans, false); + }); + + test('randomBoolean(1) should always return true', (): void => { + const booleans: boolean[] = []; + + for (let i: number = 0; i < testRepeatTotal; i++) { + booleans.push(Random.randomBoolean(1)); + } + + validateRandomBooleans(booleans, true); + }); + + describe('Chance of true validation', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-number type inputs', + inputs: [...nonNumberInputs.filter((input: unknown): boolean => input !== undefined)], + expected: TypeError + }, + { + label: 'Non-finite number inputs', + inputs: [...nonFiniteNumberInputs], + expected: TypeError + }, + { + label: 'Out of range finite number inputs', + inputs: [ + -Number.EPSILON, + 1 + Number.EPSILON, + Number.MIN_SAFE_INTEGER, + Number.MAX_SAFE_INTEGER, + -1, + 2, + -10, + 10 + ], + expected: RangeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - randomBoolean($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(() => Random.randomBoolean(testInput as number)).toThrow(testExpected); + }); + }); + }); + }); + + describe('randomElement', (): void => { + describe('randomElement should return an element from the given list with the proper element type', (): void => { + test.each([ + { + input: [1, 2, 3, 4, 5], + type: 'number' + }, + { + input: [1.1, 2.2, 3.3, 4.4, 5.5], + type: 'number' + }, + { + input: [1], + type: 'number' + }, + { + input: ['it', 'was', 'the', 'best', 'of', 'times'], + type: 'string' + }, + { + input: ['see', 'spot', 'run'], + type: 'string' + }, + { + input: ['hello'], + type: 'string' + } + ])('%# - randomElement($input) should return an element from ($input)', ({ input, type }: { input: unknown[]; type: string; }): void => { + const selected: unknown[] = []; + const repeatTotal: number = Math.max(testRepeatTotal, input.length * 6); + + for (let i: number = 0; i < repeatTotal; i++) { + selected.push(Random.randomElement(input)); + } + + validateRandomElements(selected, input, type); + }); + }); + + describe('Input validation', (): void => { + describe('Input must be a non-empty array', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-array type inputs', + inputs: [...nonArrayInputs], + expected: TypeError + }, + { + label: 'Empty array input', + inputs: [ + [] + ], + expected: TypeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - randomElement($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + Random.randomElement(testInput as unknown[]); + }).toThrow(testExpected); + }); + }); + }); + }); + }); + + describe('randomWeightedElement', (): void => { + describe('randomWeightedElement should return an element from the given list with the proper element type', (): void => { + test.each([ + { + input: [ + { value: 1, weight: 0.25 }, + { value: 2, weight: 0.25 }, + { value: 3, weight: 0.25 }, + { value: 4, weight: 0.25 } + ], + type: 'number' + }, + { + input: [ + { value: 1, weight: 0.5 }, + { value: 2, weight: 0.2 }, + { value: 3, weight: 0.2 }, + { value: 4, weight: 0.1 } + ], + type: 'number' + }, + { + input: [ + { value: 1.1, weight: 0.25 }, + { value: 2.2, weight: 0.25 }, + { value: 3.3, weight: 0.25 }, + { value: 4.4, weight: 0.25 } + ], + type: 'number' + }, + { + input: [ + { value: 1, weight: 1 } + ], + type: 'number' + }, + { + input: [ + { value: 'it', weight: 0.25 }, + { value: 'was', weight: 0.25 }, + { value: 'the', weight: 0.25 }, + { value: 'best', weight: 0.25 } + ], + type: 'string' + }, + { + input: [ + { value: 'see', weight: 0.33 }, + { value: 'spot', weight: 0.33 }, + { value: 'run', weight: 0.34 } + ], + type: 'string' + }, + { + input: [ + { value: 'hello', weight: 1 } + ], + type: 'string' + } + ])('%# - randomWeightedElement($input) should return an element from ($input)', ({ input, type }: { input: { value: unknown; weight: number; }[]; type: string; }): void => { + const selected: unknown[] = []; + const repeatTotal: number = Math.max(testRepeatTotal, input.length * 6); + const weightedElements: WeightedList = WeightedElementUtility.buildWeightedList(input); + const expectedElements: unknown[] = input.map((item: { value: unknown; weight: number; }): unknown => item.value); + + for (let i: number = 0; i < repeatTotal; i++) { + selected.push(Random.randomWeightedElement(weightedElements)); + } + + validateRandomElements(selected, expectedElements, type); + }); + }); + + describe('randomWeightedElement should not return an element from the given list if the weight is zero', (): void => { + test.each([ + { + input: [ + { value: 1, weight: 0.25 }, + { value: 2, weight: 0 }, + { value: 3, weight: 0.25 }, + { value: 4, weight: 0.25 }, + { value: 5, weight: 0.25 } + ], + expected: [1, 3, 4, 5], + type: 'number' + }, + { + input: [ + { value: 1, weight: 0.5 }, + { value: 2, weight: 0 }, + { value: 3, weight: 0.3 }, + { value: 4, weight: 0.2 } + ], + expected: [1, 3, 4], + type: 'number' + }, + { + input: [ + { value: 1, weight: 0 }, + { value: 2, weight: 0.3 }, + { value: 3, weight: 0.3 }, + { value: 4, weight: 0.4 } + ], + expected: [2, 3, 4], + type: 'number' + }, + { + input: [ + { value: 1, weight: 0.4 }, + { value: 2, weight: 0.3 }, + { value: 3, weight: 0.3 }, + { value: 4, weight: 0 } + ], + expected: [1, 2, 3], + type: 'number' + }, + { + input: [ + { value: 1.1, weight: 0 }, + { value: 2.2, weight: 0.25 }, + { value: 3.3, weight: 0.25 }, + { value: 4.4, weight: 0.5 } + ], + expected: [2.2, 3.3, 4.4], + type: 'number' + }, + { + input: [ + { value: 'it', weight: 0.2 }, + { value: 'was', weight: 0.4 }, + { value: 'the', weight: 0 }, + { value: 'best', weight: 0.4 } + ], + expected: ['it', 'was', 'best'], + type: 'string' + } + ])('%# - randomWeightedElement should not return an element from ($input) if the weight is zero', ({ input, expected, type }: { input: { value: unknown; weight: number; }[]; expected: unknown[]; type: string; }): void => { + const selected: unknown[] = []; + const repeatTotal: number = Math.max(testRepeatTotal, input.length * 10); + const weightedElements: WeightedList = WeightedElementUtility.buildWeightedList(input); + + for (let i: number = 0; i < repeatTotal; i++) { + selected.push(Random.randomWeightedElement(weightedElements)); + } + + validateRandomElements(selected, expected, type); + }); + }); + + describe('randomWeightedElement should return a fallback element if the randomNumberGenerator returns a number outside the range of 0 to 1', (): void => { + describe.each([ + { + input: [ + { value: 1, weight: 0.25 }, + { value: 2, weight: 0.25 }, + { value: 3, weight: 0.25 }, + { value: 4, weight: 0.25 } + ] + }, + { + input: [ + { value: 1, weight: 0.5 }, + { value: 2, weight: 0.2 }, + { value: 3, weight: 0.2 }, + { value: 4, weight: 0.1 } + ] + }, + { + input: [ + { value: 1.1, weight: 0.25 }, + { value: 2.2, weight: 0.25 }, + { value: 3.3, weight: 0.25 }, + { value: 4.4, weight: 0.25 } + ] + }, + { + input: [ + { value: 1, weight: 1 } + ] + }, + { + input: [ + { value: 'it', weight: 0.25 }, + { value: 'was', weight: 0.25 }, + { value: 'the', weight: 0.25 }, + { value: 'best', weight: 0.25 } + ] + }, + { + input: [ + { value: 'see', weight: 0.33 }, + { value: 'spot', weight: 0.33 }, + { value: 'run', weight: 0.34 } + ] + }, + { + input: [ + { value: 'hello', weight: 1 } + ] + }, + { + input: [ + { value: 1, weight: 0.25 }, + { value: 2, weight: 0 }, + { value: 3, weight: 0.25 }, + { value: 4, weight: 0.25 }, + { value: 5, weight: 0.25 } + ] + }, + { + input: [ + { value: 1, weight: 0.5 }, + { value: 2, weight: 0 }, + { value: 3, weight: 0.3 }, + { value: 4, weight: 0.2 } + ] + }, + { + input: [ + { value: 1, weight: 0 }, + { value: 2, weight: 0.3 }, + { value: 3, weight: 0.3 }, + { value: 4, weight: 0.4 } + ] + }, + { + input: [ + { value: 1, weight: 0.4 }, + { value: 2, weight: 0.3 }, + { value: 3, weight: 0.3 }, + { value: 4, weight: 0 } + ] + }, + { + input: [ + { value: 1.1, weight: 0 }, + { value: 2.2, weight: 0.25 }, + { value: 3.3, weight: 0.25 }, + { value: 4.4, weight: 0.5 } + ] + }, + { + input: [ + { value: 'it', weight: 0.2 }, + { value: 'was', weight: 0.4 }, + { value: 'the', weight: 0 }, + { value: 'best', weight: 0.4 } + ] + } + ])('%# - randomWeightedElement($input) with a randomNumberGenerator outside the range of 0 to 1', ({ input }: { input: { value: unknown; weight: number; }[]; }): void => { + test('Should return elements[0] if the rng function returns a negative number', (): void => { + Random.randomNumberGenerator = (): number => { + return -0.1; + }; + + const weightedElements: WeightedList = WeightedElementUtility.buildWeightedList(input); + const selected: unknown = Random.randomWeightedElement(weightedElements); + expect(selected).toBe(input[0].value); + }); + + test('Should return elements[length - 1] if the rng function returns a number greater than 1', (): void => { + Random.randomNumberGenerator = (): number => { + return 1.1; + }; + + const weightedElements: WeightedList = WeightedElementUtility.buildWeightedList(input); + const selected: unknown = Random.randomWeightedElement(weightedElements); + expect(selected).toBe(input[input.length - 1].value); + }); + }); + }); + }); + + describe('Range input validation', (): void => { + describe('Min and max range validation', (): void => { + describe('Min and max parameters must be a finite number', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-number inputs', + inputs: [...nonNumberInputs], + expected: TypeError + }, + { + label: 'Non-finite number inputs', + inputs: [...nonFiniteNumberInputs], + expected: TypeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + describe('Min parameter validation', (): void => { + test.each( + testCases + )('%# - Min ($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + Random.randomFloat(testInput as number, Number.MAX_SAFE_INTEGER); + }).toThrow(testExpected); + + expect((): void => { + Random.randomInt(testInput as number, Number.MAX_SAFE_INTEGER); + }).toThrow(testExpected); + + expect((): void => { + Random.randomInteger(testInput as number, Number.MAX_SAFE_INTEGER); + }).toThrow(testExpected); + }); + }); + + describe('Max parameter validation', (): void => { + test.each( + testCases + )('%# - Max ($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + Random.randomFloat(Number.MIN_SAFE_INTEGER, testInput as number); + }).toThrow(testExpected); + + expect((): void => { + Random.randomInt(Number.MIN_SAFE_INTEGER, testInput as number); + }).toThrow(testExpected); + + expect((): void => { + Random.randomInteger(Number.MIN_SAFE_INTEGER, testInput as number); + }).toThrow(testExpected); + }); + }); + }); + }); + + describe('Min must be less than max', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Integer min and max', + inputs: [ + { min: 0, max: -1 }, + { min: 10, max: 9 }, + { min: 10, max: 0 }, + { min: 10, max: 1 }, + { min: 10, max: -10 }, + { min: -10, max: -11 }, + { min: -10, max: -20 } + ], + expected: RangeError + }, + { + label: 'Float min and max', + inputs: [ + { min: 0.123, max: -1.123 }, + { min: 10.123, max: 9.123 }, + { min: 10.123, max: 0.123 }, + { min: 10.123, max: 1.123 }, + { min: 10.123, max: -10.123 }, + { min: -10.123, max: -11.123 }, + { min: -10.123, max: -20.123 } + ], + expected: RangeError + }, + { + label: 'Float min and max with equal floors', + inputs: [ + { min: 0.456, max: 0.123 }, + { min: 10.456, max: 10.1234 }, + { min: -10.123, max: -10.456 } + ], + expected: RangeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + describe('randomFloat', (): void => { + test.each( + testCases + )('%# - randomFloat with input ($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + const { min, max } = testInput as { min: number; max: number; }; + expect((): void => { + Random.randomFloat(min, max); + }).toThrow(testExpected); + }); + }); + + describe('randomInt', (): void => { + test.each( + testCases + )('%# - randomInt with input ($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + const { min, max } = testInput as { min: number; max: number; }; + expect((): void => { + Random.randomInt(min, max); + }).toThrow(testExpected); + }); + }); + + describe('randomInteger', (): void => { + test.each( + testCases + )('%# - randomInteger with input ($input) should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + const { min, max } = testInput as { min: number; max: number; }; + expect((): void => { + Random.randomInteger(min, max); + }).toThrow(testExpected); + }); + }); + }); + }); + }); + }); +}); diff --git a/test/random/seeded-random/random-number-generator-factory.test.ts b/test/random/seeded-random/random-number-generator-factory.test.ts index d6eb99d..1870d67 100644 --- a/test/random/seeded-random/random-number-generator-factory.test.ts +++ b/test/random/seeded-random/random-number-generator-factory.test.ts @@ -88,7 +88,7 @@ describe('RandomNumberGeneratorFactory', (): void => { }); describe('build', (): void => { - describe('build with valid inputs', (): void => { + describe('build() with valid inputs', (): void => { test.each( scenarios )('%# - $label', @@ -102,8 +102,8 @@ describe('RandomNumberGeneratorFactory', (): void => { ); }); - describe('sequence distinctness contracts', (): void => { - test('changing seed changes sequence', (): void => { + describe('Sequence distinctness contracts', (): void => { + test('Changing seed changes sequence', (): void => { const rngA: SeededRandomNumberGenerator = callBuild(asciiSeed); const rngB: SeededRandomNumberGenerator = callBuild(alternateAsciiSeed); const a: number[] = buildActualSequence(rngA, sequenceLength); @@ -111,7 +111,7 @@ describe('RandomNumberGeneratorFactory', (): void => { expect(a).not.toEqual(b); }); - test('changing namespace changes sequence', (): void => { + test('Changing namespace changes sequence', (): void => { const rngA: SeededRandomNumberGenerator = callBuild(asciiSeed, asciiNamespace); const rngB: SeededRandomNumberGenerator = callBuild(asciiSeed, alternateAsciiNamespace); const a: number[] = buildActualSequence(rngA, sequenceLength); @@ -119,7 +119,7 @@ describe('RandomNumberGeneratorFactory', (): void => { expect(a).not.toEqual(b); }); - test('changing valid version changes sequence for same seed and namespace', (): void => { + test('Changing valid version changes sequence for same seed and namespace', (): void => { const rngA: SeededRandomNumberGenerator = callBuild(asciiSeed, asciiNamespace, 0); const rngB: SeededRandomNumberGenerator = callBuild(asciiSeed, asciiNamespace, 1); const v0: number[] = buildActualSequence(rngA, sequenceLength); @@ -128,31 +128,31 @@ describe('RandomNumberGeneratorFactory', (): void => { }); }); - describe('input validation', (): void => { - describe('invalid seed inputs', (): void => { + describe('Input validation', (): void => { + describe('Invalid seed inputs', (): void => { test.each( nonStringInputs - )('%# - invalid seed %o should throw a TypeError', (seed: unknown): void => { + )('%# - Invalid seed %o should throw a TypeError', (seed: unknown): void => { expect((): void => { RandomNumberGeneratorFactory.build(seed as string); }).toThrow(TypeError); }); }); - describe('invalid namespace inputs', (): void => { + describe('Invalid namespace inputs', (): void => { test.each( nonStringInputs.filter((s: unknown): boolean => s !== undefined) - )('%# - invalid namespace %o should throw a TypeError', (namespace: unknown): void => { + )('%# - Invalid namespace %o should throw a TypeError', (namespace: unknown): void => { expect((): void => { RandomNumberGeneratorFactory.build('', namespace as string); }).toThrow(TypeError); }); }); - describe('invalid version inputs', (): void => { + describe('Invalid version inputs', (): void => { const testScenarios: Scenario[] = [ { - label: 'non-number, non-finite, and float versions', + label: 'Non-number, non-finite, and float versions', inputs: [ ...nonNumberInputs.filter((s: unknown): boolean => s !== undefined), ...nonFiniteNumberInputs, @@ -161,7 +161,7 @@ describe('RandomNumberGeneratorFactory', (): void => { expected: TypeError }, { - label: 'out-of-range integer versions', + label: 'Out-of-range integer versions', inputs: [ ...negativeIntegerInputs, SeedVersions.size, @@ -192,7 +192,7 @@ describe('RandomNumberGeneratorFactory', (): void => { }); describe('asyncBuild', (): void => { - describe('asyncBuild with valid inputs', (): void => { + describe('asyncBuild() with valid inputs', (): void => { test.each( asyncScenarios )('%# - $label', @@ -206,8 +206,8 @@ describe('RandomNumberGeneratorFactory', (): void => { ); }); - describe('sequence distinctness contracts', (): void => { - test('changing seed changes sequence', async (): Promise => { + describe('Sequence distinctness contracts', (): void => { + test('Changing seed changes sequence', async (): Promise => { const rngA: SeededRandomNumberGenerator = await callAsyncBuild(asciiSeed); const rngB: SeededRandomNumberGenerator = await callAsyncBuild(alternateAsciiSeed); const a: number[] = buildActualSequence(rngA, sequenceLength); @@ -215,7 +215,7 @@ describe('RandomNumberGeneratorFactory', (): void => { expect(a).not.toEqual(b); }); - test('changing namespace changes sequence', async (): Promise => { + test('Changing namespace changes sequence', async (): Promise => { const rngA: SeededRandomNumberGenerator = await callAsyncBuild(asciiSeed, asciiNamespace); const rngB: SeededRandomNumberGenerator = await callAsyncBuild(asciiSeed, alternateAsciiNamespace); const a: number[] = buildActualSequence(rngA, sequenceLength); @@ -224,19 +224,19 @@ describe('RandomNumberGeneratorFactory', (): void => { }); }); - describe('input validation', (): void => { - describe('invalid seed inputs', (): void => { + describe('Input validation', (): void => { + describe('Invalid seed inputs', (): void => { test.each( nonStringInputs - )('%# - invalid seed %o should throw a TypeError', async (seed: unknown): Promise => { + )('%# - Invalid seed %o should throw a TypeError', async (seed: unknown): Promise => { await expect(RandomNumberGeneratorFactory.asyncBuild(seed as string)).rejects.toThrow(TypeError); }); }); - describe('invalid namespace inputs', (): void => { + describe('Invalid namespace inputs', (): void => { test.each( nonStringInputs.filter((s: unknown): boolean => s !== undefined) - )('%# - invalid namespace %o should throw a TypeError', async (namespace: unknown): Promise => { + )('%# - Invalid namespace %o should throw a TypeError', async (namespace: unknown): Promise => { await expect(RandomNumberGeneratorFactory.asyncBuild('', namespace as string)).rejects.toThrow(TypeError); }); }); diff --git a/test/random/seeded-random/seed-versions.test.ts b/test/random/seeded-random/seed-versions.test.ts index b4c5369..dafb5c7 100644 --- a/test/random/seeded-random/seed-versions.test.ts +++ b/test/random/seeded-random/seed-versions.test.ts @@ -62,7 +62,7 @@ describe('SeedVersions', () => { }); describe('size', () => { - test(`SeedVersions.size should be ${expectedSeedVersions.length}`, () => { + test(`Size should be ${expectedSeedVersions.length}`, () => { expect(SeedVersions.size).toBe(expectedSeedVersions.length); }); }); @@ -99,7 +99,7 @@ describe('SeedVersions', () => { test.each( testCases - )('%# - $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect(SeedVersions.isValidIndex(testInput as number)).toBe(testExpected); }); }); @@ -112,7 +112,7 @@ describe('SeedVersions', () => { expect(SeedVersions.getVersion(index)).toEqual(expectedSeedVersions[index]); }); - describe('input validation', (): void => { + describe('Input validation', (): void => { test.each([ ...nonNumberInputs, expectedSeedVersions.length, diff --git a/test/random/seeded-random/seeded-random-number-generator.test.ts b/test/random/seeded-random/seeded-random-number-generator.test.ts index d7b6d80..6946778 100644 --- a/test/random/seeded-random/seeded-random-number-generator.test.ts +++ b/test/random/seeded-random/seeded-random-number-generator.test.ts @@ -27,17 +27,17 @@ import { Scenario, TestCase, buildTestCases } from '../../utils/test-case/test-c describe('SeededRandomNumberGenerator', () => { describe('new SeededRandomNumberGenerator()', () => { - describe('input validation', (): void => { + describe('Input validation', (): void => { const scenarios: Scenario[] = [ { - label: 'non-array inputs', + label: 'Non-array inputs', inputs: [ ...nonArrayInputs ], expected: TypeError }, { - label: 'array inputs with incorrect length', + label: 'Array inputs with incorrect length', inputs: [ [], [1], @@ -48,7 +48,7 @@ describe('SeededRandomNumberGenerator', () => { expected: TypeError }, { - label: 'array inputs with non-integer elements', + label: 'Array inputs with non-integer elements', inputs: [ [1.5, 0, 0, 0], [0, 1.5, 0, 0], @@ -58,7 +58,7 @@ describe('SeededRandomNumberGenerator', () => { expected: RangeError }, { - label: 'array inputs greater than 0xFFFFFFFF (max 32-bit unsigned integer)', + label: 'Array inputs greater than 0xFFFFFFFF (max 32-bit unsigned integer)', inputs: [ [(0xFFFFFFFF + 1), 0, 0, 0], [0, (0xFFFFFFFF + 1), 0, 0], @@ -68,7 +68,7 @@ describe('SeededRandomNumberGenerator', () => { expected: RangeError }, { - label: 'array inputs with negative elements', + label: 'Array inputs with negative elements', inputs: [ [-1, 0, 0, 0], [0, -1, 0, 0], @@ -78,7 +78,7 @@ describe('SeededRandomNumberGenerator', () => { expected: RangeError }, { - label: 'array input with zero state (all elements are 0)', + label: 'Array input with zero state (all elements are 0)', inputs: [[0, 0, 0, 0]], expected: RangeError } @@ -91,19 +91,19 @@ describe('SeededRandomNumberGenerator', () => { test.each( testCases - )('%# - invalid state $input should throw an error $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Invalid state $input should throw an error $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect((): SeededRandomNumberGenerator => new SeededRandomNumberGenerator(testInput as [number, number, number, number])).toThrow(testExpected); }); }); }); - describe('valid state', (): void => { + describe('Valid state', (): void => { test.each([ { state: [1, 0, 0, 0] }, { state: [0, 1, 0, 0] }, { state: [0, 0, 1, 0] }, { state: [0, 0, 0, 1] } - ])('%# - state %o should construct successfully', ({ state }: { state: number[]; }): void => { + ])('%# - State %o should construct successfully', ({ state }: { state: number[]; }): void => { expect((): SeededRandomNumberGenerator => new SeededRandomNumberGenerator(state as [number, number, number, number])).not.toThrow(); }); }); diff --git a/test/random/weighted-element/weighted-element-utility.test.ts b/test/random/weighted-element/weighted-element-utility.test.ts new file mode 100644 index 0000000..2b513bf --- /dev/null +++ b/test/random/weighted-element/weighted-element-utility.test.ts @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { Static, Type } from 'typebox'; +import { describe, expect, expectTypeOf, test } from 'vitest'; + +import { + Discriminators, + WeightedElement, + weightedElementSchema, + WeightedElementUtility, + WeightedList +} from '../../../src'; + +import { nonArrayInputs } from '../../utils/input/array-inputs'; +import { nonFunctionInputs } from '../../utils/input/function-inputs'; +import { nonFiniteNumberInputs, nonNumberInputs } from '../../utils/input/number-inputs'; +import { nonObjectInputs } from '../../utils/input/object-inputs'; +import { buildTestCases, Scenario, TestCase } from '../../utils/test-case/test-case'; + +describe('WeightedElementUtility', (): void => { + describe('new WeightedElementUtility()', (): void => { + describe('Runtime behavior guards', (): void => { + test('Constructor should throw an error when instantiated at runtime', (): void => { + const RuntimeConstructor = WeightedElementUtility as unknown as new () => WeightedElementUtility; + expect((): WeightedElementUtility => new RuntimeConstructor()).toThrow(Error); + }); + }); + }); + + describe('buildWeightedElement', (): void => { + test('buildWeightedElement() should return a typed weighed element', (): void => { + const element: WeightedElement = WeightedElementUtility.buildWeightedElement({ value: 'test value', weight: 0.5 }); + + expect(WeightedElementUtility.isWeightedElement(element, (input: unknown): input is string => typeof input === 'string')).toBe(true); + expect(WeightedElementUtility.isWeightedElement(element, (input: unknown): input is number => typeof input === 'number')).toBe(false); + }); + + describe('Input validation', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-object type inputs', + inputs: [ + ...nonObjectInputs + ], + expected: TypeError + }, + { + label: 'Array type inputs', + inputs: [ + [], + [1, 2, 3], + ['a', 'b', 'c'] + ], + expected: TypeError + }, + { + label: 'Object inputs missing value property', + inputs: [ + { weight: 0 }, + { weight: 0.5 }, + { weight: 1 } + ], + expected: TypeError + }, + { + label: 'Object inputs missing weight property', + inputs: [ + { value: 10 }, + { value: 'hello' }, + { + value: (): number => { + return 100; + } + } + ], + expected: TypeError + }, + { + label: 'Object inputs with non-numeric weight property', + inputs: [ + ...nonNumberInputs.map((input) => { + return { value: 'test', weight: input }; + }) + ], + expected: TypeError + }, + { + label: 'Object inputs with non-finite weight property', + inputs: [ + ...nonFiniteNumberInputs.map((input) => { + return { value: 'test', weight: input }; + }) + ], + expected: TypeError + }, + { + label: 'Object inputs with out of range weight property', + inputs: [ + { value: 10, weight: -5 }, + { value: 10, weight: -1 }, + { value: 10, weight: -0.1 }, + { value: 10, weight: -Number.EPSILON }, + { value: 10, weight: 1 + Number.EPSILON }, + { value: 10, weight: 1.1 }, + { value: 10, weight: 5 } + ], + expected: TypeError + }, + { + label: 'Object inputs with additional properties', + inputs: [ + { value: 'hello', weight: 0, name: 'bob' }, + { value: 'hello', weight: 0.5, age: 42 }, + { value: 'hello', weight: 1, day: 7 } + ], + expected: TypeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + WeightedElementUtility.buildWeightedElement(testInput as { value: unknown; weight: number; }); + }).toThrow(testExpected); + }); + }); + }); + }); + + describe('buildWeightedList', (): void => { + test('buildWeightedList() should return a typed weighted list', (): void => { + const list: WeightedList = WeightedElementUtility.buildWeightedList([ + { value: 'test value 1', weight: 0.5 }, + { value: 'test value 2', weight: 0.5 } + ]); + + expect(WeightedElementUtility.isWeightedList(list, (input: unknown): input is string => typeof input === 'string')).toBe(true); + expect(WeightedElementUtility.isWeightedList(list, (input: unknown): input is number => typeof input === 'number')).toBe(false); + }); + + const scenarios: Scenario[] = [ + { + label: 'Non-array type inputs', + inputs: [ + ...nonArrayInputs + ], + expected: TypeError + }, + { + label: 'Empty array input', + inputs: [ + [] + ], + expected: TypeError + }, + { + label: 'Incorrect type array input', + inputs: [ + [1, 2, 3], + ['a', 'b', 'c'], + [ + { weight: 1 } + ], + [ + { value: 'hello' } + ], + [ + { value: 'hello', weight: 'three' } + ], + [ + { value: 'hello', weight: NaN } + ], + [ + { value: 'hello', weight: Infinity } + ], + [ + { value: 10, weight: -0.1 } + ], + [ + { value: 'hello', weight: 1, name: 'bob' } + ] + ], + expected: TypeError + }, + { + label: 'Weight sum is not equal to one', + inputs: [ + [ + { value: 'test 1', weight: 0.5 }, + { value: 'test 2', weight: 0.5 }, + { value: 'test 3', weight: 0.0001 } + ], + [ + { value: 'test 1', weight: 0.5 }, + { value: 'test 2', weight: 0.5 - 0.0001 } + ], + [ + { value: 'test 1', weight: 0.5 }, + { value: 'test 2', weight: 0.5 + 0.0001 } + ], + [ + { value: 'test 1', weight: 0 } + ], + [ + { value: 'test 1', weight: 1 - 0.0001 } + ], + [ + { value: 'test 1', weight: 1 }, + { value: 'test 2', weight: 1 } + ], + [ + [ + { value: 'test 1', weight: 1 }, + { value: 'test 2', weight: 0.0001 } + ] + ] + ], + expected: TypeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + WeightedElementUtility.buildWeightedList(testInput as { value: unknown; weight: number; }[]); + }).toThrow(testExpected); + }); + }); + }); + + describe('isGenericWeightedElement()', (): void => { + describe('weightedElementSchema and WeightedElement interface should be equivalent', (): void => { + const numberSchema = Type.Call(weightedElementSchema, [Type.Number()]); + type NumberStatic = Static; + const stringSchema = Type.Call(weightedElementSchema, [Type.String()]); + type StringStatic = Static; + + test('String type', (): void => { + expect(stringSchema).toBeDefined(); + + expectTypeOf().toExtend>(); + expectTypeOf>().toExtend(); + + expectTypeOf().not.toExtend>(); + expectTypeOf>().not.toExtend(); + }); + + test('Number type', (): void => { + expect(numberSchema).toBeDefined(); + + expectTypeOf().toExtend>(); + expectTypeOf>().toExtend(); + + expectTypeOf().not.toExtend>(); + expectTypeOf>().not.toExtend(); + }); + }); + + describe('Input validation', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-object type inputs', + inputs: [ + ...nonObjectInputs + ], + expected: false + }, + { + label: 'Array type inputs', + inputs: [ + [], + [1, 2, 3], + ['a', 'b', 'c'] + ], + expected: false + }, + { + label: 'Object inputs missing value property', + inputs: [ + { weight: 0, discriminator: Discriminators.WeightedElement }, + { weight: 0.5, discriminator: Discriminators.WeightedElement }, + { weight: 1, discriminator: Discriminators.WeightedElement } + ], + expected: false + }, + { + label: 'Object inputs missing weight property', + inputs: [ + { value: 10, discriminator: Discriminators.WeightedElement }, + { value: 'hello', discriminator: Discriminators.WeightedElement }, + { + value: (): number => { + return 100; + }, + discriminator: Discriminators.WeightedElement + } + ], + expected: false + }, + { + label: 'Object inputs missing discriminator property', + inputs: [ + { value: 'hi', weight: 0 }, + { value: 'hello', weight: 0.5 }, + { value: 'test', weight: 1 } + ], + expected: false + }, + { + label: 'Object inputs with non-numeric weight property', + inputs: [ + ...nonNumberInputs.map((input) => { + return { value: 'test', weight: input, discriminator: Discriminators.WeightedElement }; + }) + ], + expected: false + }, + { + label: 'Object inputs with non-finite weight property', + inputs: [ + ...nonFiniteNumberInputs.map((input) => { + return { value: 'test', weight: input, discriminator: Discriminators.WeightedElement }; + }) + ], + expected: false + }, + { + label: 'Object inputs with out of range weight property', + inputs: [ + { value: 10, weight: -5, discriminator: Discriminators.WeightedElement }, + { value: 10, weight: -1, discriminator: Discriminators.WeightedElement }, + { value: 10, weight: -0.1, discriminator: Discriminators.WeightedElement }, + { value: 10, weight: -Number.EPSILON, discriminator: Discriminators.WeightedElement }, + { value: 10, weight: 1 + Number.EPSILON, discriminator: Discriminators.WeightedElement }, + { value: 10, weight: 1.1, discriminator: Discriminators.WeightedElement }, + { value: 10, weight: 5, discriminator: Discriminators.WeightedElement } + ], + expected: false + }, + { + label: 'Object inputs with additional properties', + inputs: [ + { value: 'hello', weight: 0, name: 'bob', discriminator: Discriminators.WeightedElement }, + { value: 'hello', weight: 0.5, age: 42, discriminator: Discriminators.WeightedElement }, + { value: 'hello', weight: 1, day: 7, discriminator: Discriminators.WeightedElement } + ], + expected: false + }, + { + label: 'Object inputs with incorrect discriminator', + inputs: [ + { value: 'hello', weight: 0, discriminator: 'invalid' }, + { value: 'hello', weight: 0.5, discriminator: '' }, + { value: 'hello', weight: 1, discriminator: 'other discriminator' } + ], + expected: false + }, + { + label: 'Valid weighted element object', + inputs: [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement }, + { value: 'hey', weight: 1, discriminator: Discriminators.WeightedElement } + ], + expected: true + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(WeightedElementUtility.isGenericWeightedElement(testInput)).toBe(testExpected); + }); + }); + }); + }); + + describe('isWeightedElement', (): void => { + describe('Input validation', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-function type inputs', + inputs: [ + ...nonFunctionInputs + ], + expected: TypeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + WeightedElementUtility.isWeightedElement({}, testInput as ((value: unknown) => value is unknown)); + }).toThrow(testExpected); + }); + }); + }); + }); + + describe('isGenericWeightedList', (): void => { + describe('Input validation', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-array type inputs', + inputs: [ + ...nonArrayInputs + ], + expected: false + }, + { + label: 'Empty array inputs', + inputs: [ + [] + ], + expected: false + }, + { + label: 'Array inputs do not contain weighted elements', + inputs: [ + [1, 2, 3], + ['a', 'b', 'c'] + ], + expected: false + }, + { + label: 'Array inputs contain weighted elements and other type', + inputs: [ + [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement }, + 500 + ], + [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement }, + 'string value' + ], + [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement }, + { key: 'value' } + ], + [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement }, + { key: 'value', discriminator: Discriminators.WeightedElement } + ] + ], + expected: false + }, + { + label: 'Weighted elements weight sum is not equal to 1', + inputs: [ + [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0, discriminator: Discriminators.WeightedElement } + ], + [ + { value: 'hello', weight: 0, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement } + ], + [ + { value: 'hello', weight: 0.5, discriminator: Discriminators.WeightedElement }, + { value: 'hi', weight: 0.5, discriminator: Discriminators.WeightedElement }, + { value: 'howdy', weight: 0.0001, discriminator: Discriminators.WeightedElement } + ] + ], + expected: false + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(WeightedElementUtility.isGenericWeightedList(testInput)).toBe(testExpected); + }); + }); + }); + }); + + describe('isWeightedList', (): void => { + describe('Input validation', (): void => { + describe('Value type guard function', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-function type inputs', + inputs: [ + ...nonFunctionInputs + ], + expected: TypeError + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should throw $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect((): void => { + WeightedElementUtility.isWeightedList([{ value: 'test', weight: 1, discriminator: Discriminators.WeightedElement }], testInput as ((value: unknown) => value is unknown)); + }).toThrow(testExpected); + }); + }); + }); + + describe('Invalid generic weighted list', (): void => { + test('Invalid generic weighted list should return false', (): void => { + expect(WeightedElementUtility.isWeightedList([], (input: unknown): input is WeightedElement => input === undefined)).toBe(false); + }); + }); + }); + }); +}); diff --git a/test/string/color-string-utility.test.ts b/test/string/color-string-utility.test.ts new file mode 100644 index 0000000..c7e8234 --- /dev/null +++ b/test/string/color-string-utility.test.ts @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { describe, test, expect } from 'vitest'; + +import { ColorStringUtility } from '../../src'; + +import { + hexColorFailureInputs, + hexColorInputs, + hexColorMixedCaseInputs, + hexColorRGBALowercaseInputs, + hexColorRGBANumberInputs, + hexColorRGBAUppercaseInputs, + hexColorRGBLowercaseInputs, + hexColorRGBNumberInputs, + hexColorRGBUppercaseInputs +} from '../utils/input/color-string-inputs'; + +import { emptyStringInputs, nonStringInputs } from '../utils/input/string-inputs'; +import { Scenario, TestCase, buildTestCases } from '../utils/test-case/test-case'; + +describe('ColorStringUtility', (): void => { + describe('new ColorStringUtility()', (): void => { + describe('Runtime behavior guards', (): void => { + test('Constructor should throw an error when instantiated at runtime', (): void => { + const RuntimeConstructor = ColorStringUtility as unknown as new () => ColorStringUtility; + expect((): ColorStringUtility => new RuntimeConstructor()).toThrow(Error); + }); + }); + }); + + describe('isHexColor', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-string inputs', + inputs: [ + ...nonStringInputs + ], + expected: false + }, + { + label: 'Empty string inputs', + inputs: [ + ...emptyStringInputs + ], + expected: false + }, + { + label: 'Hex color string failure inputs', + inputs: [ + ...hexColorFailureInputs, + ...hexColorMixedCaseInputs + ], + expected: false + }, + { + label: 'Hex color inputs', + inputs: [ + ...hexColorInputs + ], + expected: true + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(ColorStringUtility.isHexColor(testInput)).toBe(testExpected); + }); + }); + }); + + describe('isHexColorRGB', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-string inputs', + inputs: [ + ...nonStringInputs + ], + expected: false + }, + { + label: 'Empty string inputs', + inputs: [ + ...emptyStringInputs + ], + expected: false + }, + { + label: 'Hex color string failure inputs', + inputs: [ + ...hexColorFailureInputs, + ...hexColorMixedCaseInputs + ], + expected: false + }, + { + label: 'RGBA hex color inputs', + inputs: [ + ...hexColorRGBANumberInputs, + ...hexColorRGBALowercaseInputs, + ...hexColorRGBAUppercaseInputs + ], + expected: false + }, + { + label: 'RGB hex color inputs', + inputs: [ + ...hexColorRGBNumberInputs, + ...hexColorRGBLowercaseInputs, + ...hexColorRGBUppercaseInputs + ], + expected: true + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(ColorStringUtility.isHexColorRGB(testInput)).toBe(testExpected); + }); + }); + }); + + describe('isHexColorRGBA', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-string inputs', + inputs: [ + ...nonStringInputs + ], + expected: false + }, + { + label: 'Empty string inputs', + inputs: [ + ...emptyStringInputs + ], + expected: false + }, + { + label: 'Hex color string failure inputs', + inputs: [ + ...hexColorFailureInputs, + ...hexColorMixedCaseInputs + ], + expected: false + }, + { + label: 'RGBA hex color inputs', + inputs: [ + ...hexColorRGBANumberInputs, + ...hexColorRGBALowercaseInputs, + ...hexColorRGBAUppercaseInputs + ], + expected: true + }, + { + label: 'RGB hex color inputs', + inputs: [ + ...hexColorRGBNumberInputs, + ...hexColorRGBLowercaseInputs, + ...hexColorRGBUppercaseInputs + ], + expected: false + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(ColorStringUtility.isHexColorRGBA(testInput)).toBe(testExpected); + }); + }); + }); +}); diff --git a/test/string/string-utility.test.ts b/test/string/string-utility.test.ts index 5657eb2..e22d70a 100644 --- a/test/string/string-utility.test.ts +++ b/test/string/string-utility.test.ts @@ -26,11 +26,15 @@ import { emptyStringInputs, nonEmptyStringInputs, nonStringInputs, - numberAndSymbolTrimmedInputs, - singleLineLowercaseTrimmedFailureInputs, - singleLineLowercaseTrimmedInputs, - singleLineMixedCaseTrimmedInputs, - singleLineUppercaseTrimmedInputs + singleLineTrimmedInputsNumsAndSymbols, + singleLineTrimmedFailureInputsLowercase, + singleLineTrimmedInputsLowercase, + singleLineTrimmedInputsMixedCase, + singleLineTrimmedInputsUppercase, + singleLineTrimmedFailureInputsUppercase, + singleLineTrimmedFailureInputsMixedCase, + singleLineTrimmedFailureInputs, + singleLineTrimmedInputs } from '../utils/input/string-inputs'; import { Scenario, TestCase, buildTestCases } from '../utils/test-case/test-case'; @@ -66,12 +70,44 @@ describe('StringUtility', (): void => { test.each( testCases - )('%# - $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect(StringUtility.isString(testInput)).toBe(testExpected); }); }); }); + describe('isNonEmptyString', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-string inputs', + inputs: [...nonStringInputs], + expected: false + }, + { + label: 'Empty string inputs', + inputs: [...emptyStringInputs], + expected: false + }, + { + label: 'Non-empty string inputs', + inputs: [...nonEmptyStringInputs], + expected: true + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(StringUtility.isNonEmptyString(testInput)).toBe(testExpected); + }); + }); + }); + describe('isSingleLineLowercaseTrimmedString', (): void => { const scenarios: Scenario[] = [ { @@ -87,24 +123,28 @@ describe('StringUtility', (): void => { { label: 'Incorrect case inputs', inputs: [ - ...singleLineUppercaseTrimmedInputs, - ...singleLineMixedCaseTrimmedInputs + ...singleLineTrimmedInputsUppercase, + ...singleLineTrimmedInputsMixedCase, + ...singleLineTrimmedFailureInputsUppercase, + ...singleLineTrimmedFailureInputsMixedCase ], expected: false }, { label: 'Whitespace failure inputs', - inputs: [...singleLineLowercaseTrimmedFailureInputs], + inputs: [ + ...singleLineTrimmedFailureInputsLowercase + ], expected: false }, { label: 'Single line lowercase trimmed inputs', - inputs: [...singleLineLowercaseTrimmedInputs], + inputs: [...singleLineTrimmedInputsLowercase], expected: true }, { label: 'Number and symbol trimmed inputs', - inputs: [...numberAndSymbolTrimmedInputs], + inputs: [...singleLineTrimmedInputsNumsAndSymbols], expected: true } ]; @@ -116,9 +156,102 @@ describe('StringUtility', (): void => { test.each( testCases - )('%# - $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { expect(StringUtility.isSingleLineLowercaseTrimmedString(testInput)).toBe(testExpected); }); }); }); + + describe('isSingleLineUppercaseTrimmedString', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-string inputs', + inputs: [...nonStringInputs], + expected: false + }, + { + label: 'Empty string inputs', + inputs: [...emptyStringInputs], + expected: false + }, + { + label: 'Incorrect case inputs', + inputs: [ + ...singleLineTrimmedInputsLowercase, + ...singleLineTrimmedInputsMixedCase, + ...singleLineTrimmedFailureInputsLowercase, + ...singleLineTrimmedFailureInputsMixedCase + ], + expected: false + }, + { + label: 'Whitespace failure inputs', + inputs: [ + ...singleLineTrimmedFailureInputsUppercase + ], + expected: false + }, + { + label: 'Single line uppercase trimmed inputs', + inputs: [...singleLineTrimmedInputsUppercase], + expected: true + }, + { + label: 'Number and symbol trimmed inputs', + inputs: [...singleLineTrimmedInputsNumsAndSymbols], + expected: true + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(StringUtility.isSingleLineUppercaseTrimmedString(testInput)).toBe(testExpected); + }); + }); + }); + + describe('isSingleLineTrimmedString', (): void => { + const scenarios: Scenario[] = [ + { + label: 'Non-string inputs', + inputs: [...nonStringInputs], + expected: false + }, + { + label: 'Empty string inputs', + inputs: [...emptyStringInputs], + expected: false + }, + { + label: 'Whitespace failure inputs', + inputs: [ + ...singleLineTrimmedFailureInputs + ], + expected: false + }, + { + label: 'Single line trimmed inputs', + inputs: [...singleLineTrimmedInputs], + expected: true + } + ]; + + describe.each( + scenarios + )('%# - $label', ({ inputs: scenarioInputs, expected: scenarioExpected }: Scenario): void => { + const testCases: TestCase[] = buildTestCases(scenarioInputs, scenarioExpected); + + test.each( + testCases + )('%# - Input $input should return $expected', ({ input: testInput, expected: testExpected }: TestCase): void => { + expect(StringUtility.isSingleLineTrimmedString(testInput)).toBe(testExpected); + }); + }); + }); }); diff --git a/test/utils/input/color-string-inputs.ts b/test/utils/input/color-string-inputs.ts new file mode 100644 index 0000000..35261d0 --- /dev/null +++ b/test/utils/input/color-string-inputs.ts @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2026 Brittni Watkins. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +export const hexColorRGBNumberInputs: string[] = [ + '#000000', + '#999999', + '#123456', + '#987654' +]; + +export const hexColorRGBANumberInputs: string[] = [ + '#00000000', + '#99999999', + '#12345678', + '#98765432' +]; + +export const hexColorRGBUppercaseInputs: string[] = [ + '#AAAAAA', + '#FFFFFF', + '#ABCDEF', + '#1A2B3C' +]; + +export const hexColorRGBAUppercaseInputs: string[] = [ + '#AAAAAAAA', + '#FFFFFFFF', + '#ABCDEFBC', + '#1A2B3C4D' +]; + +export const hexColorRGBLowercaseInputs: string[] = [ + '#aaaaaa', + '#ffffff', + '#abcdef', + '#1a2b3c' +]; + +export const hexColorRGBALowercaseInputs: string[] = [ + '#aaaaaaaa', + '#ffffffff', + '#abcdefbc', + '#1a2b3c4d' +]; + +export const hexColorRGBMixedCaseInputs: string[] = [ + '#aAaAaA', + '#FfFfFf', + '#AbcdeF', + '#1a2B3c' +]; + +export const hexColorRGBAMixedCaseInputs: string[] = [ + '#aAaAaAaA', + '#FfFfFfFf', + '#AbcdefaB', + '#1a2B3c4D' +]; + +export const hexColorInputs: string[] = [ + ...hexColorRGBNumberInputs, + ...hexColorRGBANumberInputs, + ...hexColorRGBUppercaseInputs, + ...hexColorRGBAUppercaseInputs, + ...hexColorRGBLowercaseInputs, + ...hexColorRGBALowercaseInputs +]; + +export const hexColorMixedCaseInputs: string[] = [ + ...hexColorRGBMixedCaseInputs, + ...hexColorRGBAMixedCaseInputs +]; + +export const hexColorFailureInputs: string[] = [ + '000000', + '999999', + '123456', + '987654', + 'AAAAAA', + 'FFFFFF', + 'ABCDEF', + '1A2B3C', + 'aaaaaa', + 'ffffff', + 'abcdef', + '1a2b3c', + '#000', + '#AAA', + '#FFF', + '#1A2', + '#aaa', + '#fff', + '#abc', + '#1a2', + '#00', + '#AA', + '#FF', + '#1A', + '#aa', + '#ff', + '#ab', + '#1a', + '#0000000', + '#9999999', + '#1234567', + '#9876543', + '#AAAAAAA', + '#FFFFFFF', + '#ABCDEFB', + '#1A2B3C4', + '#aaaaaaa', + '#fffffff', + '#abcdefb', + '#1a2b3c4', + '#000000000', + '#999999999', + '#123456789', + '#987654321', + '#AAAAAAAAA', + '#FFFFFFFFF', + '#ABCDEFBCD', + '#1A2B3C4D5', + '#aaaaaaaaa', + '#fffffffff', + '#abcdefbcd', + '#1a2b3c4d5', + '#00000G', + '#99999H', + '#12345Z', + '#98765\u{1F3A8}', + '#AAAAA!', + '#FFFFF@', + '#ABCDE?', + '#1A2B3]', + '#aaaaaë', + '#fffffg', + '#abcde\u{1F3A8}', + '#1a2b3$', + '#0000000G', + '#9999999H', + '#1234567Z', + '#9876543\u{1F3A8}', + '#AAAAAAA!', + '#FFFFFFF@', + '#ABCDEFB?', + '#1A2B3C4]', + '#aaaaaaaë', + '#fffffffg', + '#abcdefb\u{1F3A8}', + '#1a2b3c4$', + '#000 000', + '#99 9999', + '#123 456', + '#987 654', + '#AAAA AA', + '# FFFFFF', + '#ABCDE F', + '#1A2 3C', + '#aaaa aa', + '# ffffff', + ' #abcdef', + '#1a2b3c ', + '#00\n0000', + '#9999\n99', + '#12345\t6', + '#987654\n', + '#AAAAAA ', + '#F\nFFFFF', + '\n\t#ABCDEF', + '#1\tA2B3C', + '#aaa\n\taaa', + '#ffffff\n\t', + ' #abcdef', + '#1a2b3c ' +]; diff --git a/test/utils/input/string-inputs.ts b/test/utils/input/string-inputs.ts index 97cb96f..ddb319e 100644 --- a/test/utils/input/string-inputs.ts +++ b/test/utils/input/string-inputs.ts @@ -18,6 +18,8 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { hexColorFailureInputs, hexColorInputs, hexColorMixedCaseInputs } from './color-string-inputs'; + // noinspection JSPrimitiveTypeWrapperUsage export const nonStringInputs: unknown[] = [ null, @@ -74,7 +76,7 @@ export const emptyStringInputs: string[] = [ ' \n\t ' ]; -export const numberAndSymbolTrimmedInputs: string[] = [ +export const singleLineTrimmedInputsNumsAndSymbols: string[] = [ '\u{1F3A8}', '\u{1F3A8} \u{1F3A8}', '🎨', @@ -84,7 +86,7 @@ export const numberAndSymbolTrimmedInputs: string[] = [ '!@#$%^&*()-_+=~`"\'/\\|,.<>?;:' ]; -export const singleLineMixedCaseTrimmedInputs: string[] = [ +export const singleLineTrimmedInputsMixedCase: string[] = [ 'Example', 'ëË', 'ë Ë', @@ -93,7 +95,7 @@ export const singleLineMixedCaseTrimmedInputs: string[] = [ 'this IS an Example sEnTeNcE!' ]; -export const singleLineLowercaseTrimmedInputs: string[] = [ +export const singleLineTrimmedInputsLowercase: string[] = [ 'example', 'ë', 'ë ë', @@ -102,7 +104,7 @@ export const singleLineLowercaseTrimmedInputs: string[] = [ 'this is an example sentence!' ]; -export const singleLineUppercaseTrimmedInputs: string[] = [ +export const singleLineTrimmedInputsUppercase: string[] = [ 'EXAMPLE', 'Ë', 'Ë Ë', @@ -111,7 +113,7 @@ export const singleLineUppercaseTrimmedInputs: string[] = [ 'THIS IS AN EXAMPLE SENTENCE!' ]; -export const singleLineLowercaseTrimmedFailureInputs: string[] = [ +export const singleLineTrimmedFailureInputsLowercase: string[] = [ ' example ', '\nexample\n', '\texample\t', @@ -131,10 +133,13 @@ export const singleLineLowercaseTrimmedFailureInputs: string[] = [ 'internal\n spaces\n example', 'internal\t spaces\t example', 'internal spaces example', - 'internal spaces example' + 'internal spaces example', + '\nëë', + 'ë\në', + 'ëë\n' ]; -export const singleLineUppercaseTrimmedFailureInputs: string[] = [ +export const singleLineTrimmedFailureInputsUppercase: string[] = [ ' EXAMPLE ', '\nEXAMPLE\n', '\tEXAMPLE\t', @@ -154,10 +159,13 @@ export const singleLineUppercaseTrimmedFailureInputs: string[] = [ 'INTERNAL\n SPACES\n EXAMPLE', 'INTERNAL\t SPACES\t EXAMPLE', 'INTERNAL SPACES EXAMPLE', - 'INTERNAL SPACES EXAMPLE' + 'INTERNAL SPACES EXAMPLE', + '\nËË', + 'Ë\nË', + 'ËË\n' ]; -export const singleLineMixedCaseTrimmedFailureInputs: string[] = [ +export const singleLineTrimmedFailureInputsMixedCase: string[] = [ ' Example ', '\nExample\n', '\tExample\t', @@ -177,13 +185,23 @@ export const singleLineMixedCaseTrimmedFailureInputs: string[] = [ 'Internal\n Spaces\n Example', 'Internal\t Spaces\t Example', 'Internal Spaces Example', - 'Internal Spaces Example' + 'Internal Spaces Example', + '\nëË', + 'ë\nË', + 'ëË\n' +]; + +export const singleLineTrimmedInputs: string[] = [ + ...singleLineTrimmedInputsLowercase, + ...singleLineTrimmedInputsUppercase, + ...singleLineTrimmedInputsMixedCase, + ...singleLineTrimmedInputsNumsAndSymbols ]; export const singleLineTrimmedFailureInputs: string[] = [ - ...singleLineLowercaseTrimmedFailureInputs, - ...singleLineUppercaseTrimmedFailureInputs, - ...singleLineMixedCaseTrimmedFailureInputs + ...singleLineTrimmedFailureInputsLowercase, + ...singleLineTrimmedFailureInputsUppercase, + ...singleLineTrimmedFailureInputsMixedCase ]; export const nonEmptyStringInputs: string[] = [ @@ -197,7 +215,8 @@ export const nonEmptyStringInputs: string[] = [ '🎨', 'ë', ...singleLineTrimmedFailureInputs, - ...singleLineLowercaseTrimmedInputs, - ...singleLineUppercaseTrimmedInputs, - ...singleLineMixedCaseTrimmedInputs + ...singleLineTrimmedInputs, + ...hexColorInputs, + ...hexColorMixedCaseInputs, + ...hexColorFailureInputs ]; diff --git a/test/utils/test-case/scenarios/random-number-generator-factory-scenarios.ts b/test/utils/test-case/scenarios/random-number-generator-factory-scenarios.ts index d916b98..30dda98 100644 --- a/test/utils/test-case/scenarios/random-number-generator-factory-scenarios.ts +++ b/test/utils/test-case/scenarios/random-number-generator-factory-scenarios.ts @@ -119,13 +119,13 @@ function buildKey(seed: string, namespace?: string): string { } } -function getExpectedSequence(seed: string, namespace?: string, version?: number): number[] { +export function getExpectedSequence(seed: string, namespace?: string, version?: number): number[] { const key: string = buildKey(seed, namespace); const index: number = version ?? 0; return sequences[key][index]; } -function getExpectedAsyncSequence(seed: string, namespace?: string): number[] { +export function getExpectedAsyncSequence(seed: string, namespace?: string): number[] { const key: string = buildKey(seed, namespace); return asyncSequences[key]; } diff --git a/tsconfig.vitest.json b/tsconfig.vitest.json new file mode 100644 index 0000000..90a2bcb --- /dev/null +++ b/tsconfig.vitest.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "skipLibCheck": true + } +} diff --git a/typedoc.json b/typedoc.json index e5743d7..c4748f2 100644 --- a/typedoc.json +++ b/typedoc.json @@ -49,5 +49,5 @@ "treatWarningsAsErrors": true, - "customFooterHtml": "
                        Copyright © 2024-2026 Brittni Watkins." + "customFooterHtml": "
                        Copyright © 2022-2026 Brittni Watkins." } diff --git a/vitest.config.ts b/vitest.config.ts index 34fe808..4d1e659 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -33,6 +33,11 @@ export default defineConfig({ '_doc/**', 'docs/**' ], + typecheck: { + enabled: true, + tsconfig: './tsconfig.vitest.json', + include: ['test/**/*.{test,spec}.{ts,mts,cts,tsx}'] + }, coverage: { provider: 'v8', reporter: ['text', 'lcov', 'json', 'json-summary', 'clover', 'html'],