From f6a8c1184039ae6168b4890e094a6ffd434c45ca Mon Sep 17 00:00:00 2001 From: Jacopo Patroclo Martinelli <42748150+JacopoPatroclo@users.noreply.github.com> Date: Mon, 11 Mar 2024 06:50:48 +0900 Subject: [PATCH] Added support for jsx-runtime & moved to pnpm workspace (#129) Co-authored-by: Arthur Fiorette --- .changeset/README.md | 9 + .changeset/calm-pants-call.md | 5 + .changeset/config.json | 11 + .changeset/gold-islands-brake.md | 5 + .changeset/hot-meals-beam.md | 7 + .changeset/long-elephants-work.md | 5 + .changeset/mighty-kids-camp.md | 5 + .changeset/soft-chairs-explain.md | 6 + .github/workflows/ci.yml | 29 +- .github/workflows/codeql.yml | 4 +- .../{automerge.yml => dependabot.yml} | 0 .github/workflows/release.yml | 56 + .gitignore | 6 +- .husky/pre-commit | 12 + .npmrc | 1 + .nvmrc | 2 +- .vscode/settings.json | 1 + README.md | 1021 +----- SECURITY.md | 9 - benchmark.md | 39 - benchmark/index.ts | 90 - benchmark/package.json | 3 - benchmark/renderers/many-components.tsx | 1323 -------- benchmark/renderers/mdn-homepage.tsx | 2743 ---------------- benchmarks/README.md | 31 + benchmarks/kitajs/index.ts | 3 + benchmarks/kitajs/package.json | 13 + .../kitajs/renderers/many-components.tsx | 7 + benchmarks/kitajs/renderers/many-props.tsx | 245 ++ benchmarks/kitajs/renderers/real-world.tsx | 163 + benchmarks/kitajs/tsconfig.json | 17 + benchmarks/reactjsx/index.ts | 3 + benchmarks/reactjsx/package.json | 17 + .../reactjsx/renderers/many-components.tsx | 7 + benchmarks/reactjsx/renderers/many-props.tsx | 216 ++ benchmarks/reactjsx/renderers/real-world.tsx | 164 + benchmarks/reactjsx/tsconfig.json | 11 + benchmarks/runner/assertions.js | 51 + benchmarks/runner/index.js | 64 + benchmarks/runner/package.json | 17 + benchmarks/templates/index.ts | 3 + benchmarks/templates/package.json | 15 + .../templates/renderers/many-components.tsx | 7 + benchmarks/templates/renderers/many-props.tsx | 234 ++ benchmarks/templates/renderers/real-world.tsx | 169 + benchmarks/templates/tsconfig.json | 14 + benchmarks/typed-html/index.ts | 3 + benchmarks/typed-html/package.json | 13 + .../typed-html/renderers/many-components.tsx | 9 + .../typed-html}/renderers/many-props.tsx | 240 +- .../typed-html/renderers/real-world.tsx | 165 + benchmarks/typed-html/tsconfig.json | 15 + examples/suspense-server.tsx | 2 +- package.json | 64 +- packages/fastify-html-plugin/LICENSE | 21 + packages/fastify-html-plugin/README.md | 211 ++ .../fastify-html-plugin/assets/preview.png | Bin 0 -> 59332 bytes .../fastify-html-plugin/examples/htmx.tsx | 107 + packages/fastify-html-plugin/index.js | 138 + packages/fastify-html-plugin/lib/constants.js | 14 + packages/fastify-html-plugin/lib/is-html.js | 26 + .../fastify-html-plugin/lib/is-tag-html.js | 19 + .../lib/prepend-doctype.js | 13 + packages/fastify-html-plugin/package.json | 46 + .../fastify-html-plugin/test/auto-detect.tsx | 74 + .../fastify-html-plugin/test/index.test.tsx | 80 + .../fastify-html-plugin/test/is-html.test.tsx | 69 + .../test/prepend-doctype.test.tsx | 95 + .../test/stream-html.test.tsx | 616 ++++ packages/fastify-html-plugin/tsconfig.json | 13 + packages/fastify-html-plugin/types/index.d.ts | 174 + .../types/index.test-d.tsx | 40 + .npmignore => packages/html/.npmignore | 0 packages/html/LICENSE | 201 ++ packages/html/README.md | 1147 +++++++ .../html/all-types.d.ts | 0 all-types.js => packages/html/all-types.js | 0 alpine.d.ts => packages/html/alpine.d.ts | 0 alpine.js => packages/html/alpine.js | 0 {assets => packages/html/assets}/preview.png | Bin .../html/error-boundary.d.ts | 0 .../html/error-boundary.js | 0 .../html/hotwire-turbo.d.ts | 0 .../html/hotwire-turbo.js | 0 htmx.d.ts => packages/html/htmx.d.ts | 0 htmx.js => packages/html/htmx.js | 0 index.d.ts => packages/html/index.d.ts | 101 +- index.js => packages/html/index.js | 105 +- packages/html/jsx-runtime.d.ts | 37 + packages/html/jsx-runtime.js | 82 + jsx.d.ts => packages/html/jsx.d.ts | 15 +- jsx.js => packages/html/jsx.js | 0 packages/html/package.json | 47 + register.d.ts => packages/html/register.d.ts | 0 register.js => packages/html/register.js | 6 + suspense.d.ts => packages/html/suspense.d.ts | 0 suspense.js => packages/html/suspense.js | 6 +- {test => packages/html/test}/async.test.tsx | 18 +- .../html/test}/attributes.test.tsx | 1 - {test => packages/html/test}/compile.test.tsx | 0 .../html/test}/components.test.tsx | 0 .../html/test}/encoding.test.tsx | 1 - .../html/test}/error-boundary.test.tsx | 3 +- .../html/test}/escape-tagged-func.test.tsx | 0 {test => packages/html/test}/escape.test.tsx | 0 .../html/test}/hotwire-turbo.test.tsx | 1 - {test => packages/html/test}/misc.test.tsx | 17 +- {test => packages/html/test}/react.test.tsx | 22 + .../html/test}/register.test.tsx | 6 + packages/html/test/runtime.test.tsx | 107 + .../html/test}/simple-html.test.tsx | 1 - {test => packages/html/test}/style.test.tsx | 2 +- .../html/test}/suspense.test.tsx | 15 +- {test => packages/html/test}/tags.test.tsx | 8 +- {test => packages/html/test}/util.test.ts | 12 + packages/html/tsconfig.json | 38 + packages/ts-html-plugin/.npmignore | 8 + packages/ts-html-plugin/LICENSE | 21 + packages/ts-html-plugin/README.md | 302 ++ packages/ts-html-plugin/assets/preview.png | Bin 0 -> 55866 bytes packages/ts-html-plugin/package.json | 45 + packages/ts-html-plugin/src/cli.ts | 233 ++ packages/ts-html-plugin/src/errors.ts | 22 + packages/ts-html-plugin/src/index.ts | 43 + packages/ts-html-plugin/src/util.ts | 356 +++ packages/ts-html-plugin/test/arrays.test.ts | 19 + packages/ts-html-plugin/test/children.test.ts | 35 + .../ts-html-plugin/test/component-xss.test.ts | 119 + .../ts-html-plugin/test/double-escape.test.ts | 63 + .../ts-html-plugin/test/operators.test.ts | 126 + packages/ts-html-plugin/test/readme.test.ts | 26 + packages/ts-html-plugin/test/safe.test.ts | 69 + packages/ts-html-plugin/test/tsconfig.json | 7 + .../ts-html-plugin/test/unsafe-tags.test.ts | 74 + packages/ts-html-plugin/test/util/index.tsx | 2 + .../ts-html-plugin/test/util/lang-server.ts | 206 ++ packages/ts-html-plugin/test/warn.test.ts | 60 + packages/ts-html-plugin/tsconfig.build.json | 4 + packages/ts-html-plugin/tsconfig.json | 13 + pnpm-lock.yaml | 2822 +++++++++++++++-- pnpm-workspace.yaml | 3 + tsconfig.benchmark.json | 13 - tsconfig.json | 7 +- 143 files changed, 9974 insertions(+), 5853 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/calm-pants-call.md create mode 100644 .changeset/config.json create mode 100644 .changeset/gold-islands-brake.md create mode 100644 .changeset/hot-meals-beam.md create mode 100644 .changeset/long-elephants-work.md create mode 100644 .changeset/mighty-kids-camp.md create mode 100644 .changeset/soft-chairs-explain.md rename .github/workflows/{automerge.yml => dependabot.yml} (100%) create mode 100644 .github/workflows/release.yml create mode 100644 .husky/pre-commit create mode 100644 .npmrc delete mode 100644 SECURITY.md delete mode 100644 benchmark.md delete mode 100644 benchmark/index.ts delete mode 100644 benchmark/package.json delete mode 100644 benchmark/renderers/many-components.tsx delete mode 100644 benchmark/renderers/mdn-homepage.tsx create mode 100644 benchmarks/README.md create mode 100644 benchmarks/kitajs/index.ts create mode 100644 benchmarks/kitajs/package.json create mode 100644 benchmarks/kitajs/renderers/many-components.tsx create mode 100644 benchmarks/kitajs/renderers/many-props.tsx create mode 100644 benchmarks/kitajs/renderers/real-world.tsx create mode 100644 benchmarks/kitajs/tsconfig.json create mode 100644 benchmarks/reactjsx/index.ts create mode 100644 benchmarks/reactjsx/package.json create mode 100644 benchmarks/reactjsx/renderers/many-components.tsx create mode 100644 benchmarks/reactjsx/renderers/many-props.tsx create mode 100644 benchmarks/reactjsx/renderers/real-world.tsx create mode 100644 benchmarks/reactjsx/tsconfig.json create mode 100644 benchmarks/runner/assertions.js create mode 100644 benchmarks/runner/index.js create mode 100644 benchmarks/runner/package.json create mode 100644 benchmarks/templates/index.ts create mode 100644 benchmarks/templates/package.json create mode 100644 benchmarks/templates/renderers/many-components.tsx create mode 100644 benchmarks/templates/renderers/many-props.tsx create mode 100644 benchmarks/templates/renderers/real-world.tsx create mode 100644 benchmarks/templates/tsconfig.json create mode 100644 benchmarks/typed-html/index.ts create mode 100644 benchmarks/typed-html/package.json create mode 100644 benchmarks/typed-html/renderers/many-components.tsx rename {benchmark => benchmarks/typed-html}/renderers/many-props.tsx (50%) create mode 100644 benchmarks/typed-html/renderers/real-world.tsx create mode 100644 benchmarks/typed-html/tsconfig.json create mode 100644 packages/fastify-html-plugin/LICENSE create mode 100644 packages/fastify-html-plugin/README.md create mode 100644 packages/fastify-html-plugin/assets/preview.png create mode 100644 packages/fastify-html-plugin/examples/htmx.tsx create mode 100644 packages/fastify-html-plugin/index.js create mode 100644 packages/fastify-html-plugin/lib/constants.js create mode 100644 packages/fastify-html-plugin/lib/is-html.js create mode 100644 packages/fastify-html-plugin/lib/is-tag-html.js create mode 100644 packages/fastify-html-plugin/lib/prepend-doctype.js create mode 100644 packages/fastify-html-plugin/package.json create mode 100644 packages/fastify-html-plugin/test/auto-detect.tsx create mode 100644 packages/fastify-html-plugin/test/index.test.tsx create mode 100644 packages/fastify-html-plugin/test/is-html.test.tsx create mode 100644 packages/fastify-html-plugin/test/prepend-doctype.test.tsx create mode 100644 packages/fastify-html-plugin/test/stream-html.test.tsx create mode 100644 packages/fastify-html-plugin/tsconfig.json create mode 100644 packages/fastify-html-plugin/types/index.d.ts create mode 100644 packages/fastify-html-plugin/types/index.test-d.tsx rename .npmignore => packages/html/.npmignore (100%) create mode 100644 packages/html/LICENSE create mode 100644 packages/html/README.md rename all-types.d.ts => packages/html/all-types.d.ts (100%) rename all-types.js => packages/html/all-types.js (100%) rename alpine.d.ts => packages/html/alpine.d.ts (100%) rename alpine.js => packages/html/alpine.js (100%) rename {assets => packages/html/assets}/preview.png (100%) rename error-boundary.d.ts => packages/html/error-boundary.d.ts (100%) rename error-boundary.js => packages/html/error-boundary.js (100%) rename hotwire-turbo.d.ts => packages/html/hotwire-turbo.d.ts (100%) rename hotwire-turbo.js => packages/html/hotwire-turbo.js (100%) rename htmx.d.ts => packages/html/htmx.d.ts (100%) rename htmx.js => packages/html/htmx.js (100%) rename index.d.ts => packages/html/index.d.ts (65%) rename index.js => packages/html/index.js (88%) create mode 100644 packages/html/jsx-runtime.d.ts create mode 100644 packages/html/jsx-runtime.js rename jsx.d.ts => packages/html/jsx.d.ts (97%) rename jsx.js => packages/html/jsx.js (100%) create mode 100644 packages/html/package.json rename register.d.ts => packages/html/register.d.ts (100%) rename register.js => packages/html/register.js (60%) rename suspense.d.ts => packages/html/suspense.d.ts (100%) rename suspense.js => packages/html/suspense.js (98%) rename {test => packages/html/test}/async.test.tsx (75%) rename {test => packages/html/test}/attributes.test.tsx (99%) rename {test => packages/html/test}/compile.test.tsx (100%) rename {test => packages/html/test}/components.test.tsx (100%) rename {test => packages/html/test}/encoding.test.tsx (98%) rename {test => packages/html/test}/error-boundary.test.tsx (96%) rename {test => packages/html/test}/escape-tagged-func.test.tsx (100%) rename {test => packages/html/test}/escape.test.tsx (100%) rename {test => packages/html/test}/hotwire-turbo.test.tsx (99%) rename {test => packages/html/test}/misc.test.tsx (85%) rename {test => packages/html/test}/react.test.tsx (85%) rename {test => packages/html/test}/register.test.tsx (80%) create mode 100644 packages/html/test/runtime.test.tsx rename {test => packages/html/test}/simple-html.test.tsx (98%) rename {test => packages/html/test}/style.test.tsx (95%) rename {test => packages/html/test}/suspense.test.tsx (99%) rename {test => packages/html/test}/tags.test.tsx (90%) rename {test => packages/html/test}/util.test.ts (88%) create mode 100644 packages/html/tsconfig.json create mode 100644 packages/ts-html-plugin/.npmignore create mode 100644 packages/ts-html-plugin/LICENSE create mode 100644 packages/ts-html-plugin/README.md create mode 100644 packages/ts-html-plugin/assets/preview.png create mode 100644 packages/ts-html-plugin/package.json create mode 100644 packages/ts-html-plugin/src/cli.ts create mode 100644 packages/ts-html-plugin/src/errors.ts create mode 100644 packages/ts-html-plugin/src/index.ts create mode 100644 packages/ts-html-plugin/src/util.ts create mode 100644 packages/ts-html-plugin/test/arrays.test.ts create mode 100644 packages/ts-html-plugin/test/children.test.ts create mode 100644 packages/ts-html-plugin/test/component-xss.test.ts create mode 100644 packages/ts-html-plugin/test/double-escape.test.ts create mode 100644 packages/ts-html-plugin/test/operators.test.ts create mode 100644 packages/ts-html-plugin/test/readme.test.ts create mode 100644 packages/ts-html-plugin/test/safe.test.ts create mode 100644 packages/ts-html-plugin/test/tsconfig.json create mode 100644 packages/ts-html-plugin/test/unsafe-tags.test.ts create mode 100644 packages/ts-html-plugin/test/util/index.tsx create mode 100644 packages/ts-html-plugin/test/util/lang-server.ts create mode 100644 packages/ts-html-plugin/test/warn.test.ts create mode 100644 packages/ts-html-plugin/tsconfig.build.json create mode 100644 packages/ts-html-plugin/tsconfig.json create mode 100644 pnpm-workspace.yaml delete mode 100644 tsconfig.benchmark.json diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 000000000..5934a44aa --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,9 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a +build tool that works with multi-package repos, or single-package repos to help you +version and publish your code. You can find the full documentation for it +[in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/calm-pants-call.md b/.changeset/calm-pants-call.md new file mode 100644 index 000000000..060c1c7fb --- /dev/null +++ b/.changeset/calm-pants-call.md @@ -0,0 +1,5 @@ +--- +'@kitajs/html': patch +--- + +Added support for react-jsx runtime diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 000000000..0b4dc26ab --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": ["@changesets/changelog-github", { "repo": "kitajs/kitajs" }], + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "master", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.changeset/gold-islands-brake.md b/.changeset/gold-islands-brake.md new file mode 100644 index 000000000..55cd006dd --- /dev/null +++ b/.changeset/gold-islands-brake.md @@ -0,0 +1,5 @@ +--- +'@kitajs/html': patch +--- + +Fixed triple slash directives for jsx-runtime diff --git a/.changeset/hot-meals-beam.md b/.changeset/hot-meals-beam.md new file mode 100644 index 000000000..9f81d34e8 --- /dev/null +++ b/.changeset/hot-meals-beam.md @@ -0,0 +1,7 @@ +--- +'@kitajs/fastify-html-plugin': major +'@kitajs/html': major +'@kitajs/ts-html-plugin': major +--- + +Ported to ReactJSX runtime. diff --git a/.changeset/long-elephants-work.md b/.changeset/long-elephants-work.md new file mode 100644 index 000000000..54c1237be --- /dev/null +++ b/.changeset/long-elephants-work.md @@ -0,0 +1,5 @@ +--- +'@kitajs/fastify-html-plugin': patch +--- + +Updated Dependencies diff --git a/.changeset/mighty-kids-camp.md b/.changeset/mighty-kids-camp.md new file mode 100644 index 000000000..59c5c9137 --- /dev/null +++ b/.changeset/mighty-kids-camp.md @@ -0,0 +1,5 @@ +--- +'@kitajs/html': patch +--- + +Reserved 'key' attribute to prevent use of it diff --git a/.changeset/soft-chairs-explain.md b/.changeset/soft-chairs-explain.md new file mode 100644 index 000000000..cc20f9b14 --- /dev/null +++ b/.changeset/soft-chairs-explain.md @@ -0,0 +1,6 @@ +--- +'@kitajs/ts-html-plugin': patch +'@kitajs/html': patch +--- + +Avoid hijacking fastify reply diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6f88ae46..8bc1f1900 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,18 @@ -name: Code CI +name: CI Flow on: push: - pull_request: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: jobs: build: - name: Build and test code - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + name: CI flow ${{ matrix.os }} + + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout @@ -25,10 +30,18 @@ jobs: - name: Install packages run: pnpm install --frozen-lockfile + - name: Build + run: pnpm build + + - name: Link build + run: pnpm install --frozen-lockfile + - name: Test run: pnpm test - - name: Codecov upload - uses: codecov/codecov-action@v3 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Publish to Codecov + uses: codecov/codecov-action@v4 + with: + # Codecov backend may be unstable + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c781f0075..d48415caf 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,10 +2,10 @@ name: 'CodeQL' on: push: - branches: [main] + branches: [master] paths: ['**.js', '**.jsx', '**.ts', '**.tsx'] pull_request: - branches: [main] + branches: [master] schedule: - cron: '0 0/12 * * *' diff --git a/.github/workflows/automerge.yml b/.github/workflows/dependabot.yml similarity index 100% rename from .github/workflows/automerge.yml rename to .github/workflows/dependabot.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..d36b6d379 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release Flow + +on: + push: + branches: [master] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + release: + runs-on: ubuntu-latest + if: ${{ github.actor != 'dependabot[bot]' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # https://github.com/actions/checkout/issues/1471 + - name: Fetch tags + run: git fetch --prune --unshallow --tags + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + + - name: Setup node and restore cached dependencies + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'pnpm' + + - name: Install packages + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Link build + run: pnpm install --frozen-lockfile + + - name: Create Release Pull Request or Publish + id: changesets + uses: changesets/action@v1 + with: + version: pnpm ci:version + commit: 'chore: update versions' + title: 'Release plan' + + # https://github.com/changesets/action/issues/246 + # https://github.com/changesets/changesets/pull/674 + publish: pnpm ci:publish + env: + GITHUB_TOKEN: ${{ secrets.AUTOMERGE_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index baca9fd5c..72bdc299e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ + node_modules + index.tsbuildinfo dist -coverage \ No newline at end of file +coverage + +*.log \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..83ad370d1 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,12 @@ +#!/usr/bin/env sh + +FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g') +[ -z "$FILES" ] && exit 0 + +# Prettify all selected files +echo "$FILES" | xargs ./node_modules/.bin/prettier --ignore-unknown --write + +# Add back the modified/prettified files to staging +echo "$FILES" | xargs git add + +exit 0 \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..f3b569056 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shell-emulator=true \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index ef1520fc5..5bacb9a1f 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.7.0 \ No newline at end of file +20.8.1 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 645723ac3..afb30c3ca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { + "typescript.tsdk": "node_modules/typescript/lib", "cSpell.words": ["KITA"] } diff --git a/README.md b/README.md index cd2bd79f9..e2d4a04a7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@

- Using this package? Please consider donating to support my open source work ❤️ + Please consider donating to support my open source work ❤️
- Help @kitajs/html grow! Star and share this amazing repository with your friends and co-workers! + Help KitaJS grow! Star and share this amazing repository with your friends and co-workers!

@@ -16,1017 +16,18 @@
-
- License - Codecov - Downloads - Bundlephobia - Last commit - Stars -
- -
-
- -

🏛️ KitaJS Html

- -

- @kitajs/html is a super fast JSX runtime to generate HTML strings that works everywhere. -
Express? Fastify? Hono? Bun? Htmx? -

If your code works with strings, we got you covered. -
-
-

- -
- -- [Preview](#preview) -- [Installing](#installing) -- [Getting Started](#getting-started) -- [Sanitization](#sanitization) - - [The Safe Attribute](#the-safe-attribute) -- [Editor Intellisense and CLI tool](#editor-intellisense-and-cli-tool) -- [Async Components](#async-components) - - [Suspense component](#suspense-component) - - [Error boundaries](#error-boundaries) - - [Why JSX.Element is a Promise?](#why-jsxelement-is-a-promise) - - [Why there is no `context` API?](#why-there-is-no-context-api) -- [Migrating from HTML](#migrating-from-html) - - [Htmx](#htmx) - - [Hotwire Turbo](#hotwire-turbo) - - [Base HTML templates](#base-html-templates) -- [Compiling HTML](#compiling-html) - - [Clean Components](#clean-components) -- [Fragments](#fragments) -- [Supported HTML](#supported-html) - - [The `tag` tag](#the-tag-tag) - - [Conditional classes](#conditional-classes) -- [Extending types](#extending-types) - - [Allow everything!](#allow-everything) -- [Performance](#performance) -- [How it works](#how-it-works) -- [Format HTML output](#format-html-output) -- [Fork credits](#fork-credits) - -
-
- -## Preview - -Example of an error thrown by this LSP plugin. - -
- -## Installing - -To use the `@kitajs/html` package, follow these steps: - -1. Install the required npm packages, `@kitajs/html` and `@kitajs/ts-html-plugin`, to - enable editor intellisense. Open your terminal and run: - - ```sh - npm install @kitajs/html @kitajs/ts-html-plugin - ``` - -2. Configure your TypeScript project to transpile TSX/JSX into JavaScript and using our - [LSP Plugin](#editor-intellisense-and-cli-tool). Update your `tsconfig.json` file with - the following settings: - - ```jsonc - // tsconfig.json - - { - "compilerOptions": { - "jsx": "react", - "jsxFactory": "Html.createElement", - "jsxFragmentFactory": "Html.Fragment", - "plugins": [{ "name": "@kitajs/ts-html-plugin" }] - } - } - ``` - -3. Append the - [`xss-scan`](https://github.com/kitajs/ts-html-plugin/tree/main#running-as-cli) command - into your test script. This CLI comes from @kitajs/ts-html-plugin, which catches XSS - vulnerabilities that may be introduced into your codebase, automating the xss scanning - process to run everytime you test your code, like inside your CI/CD environment. Open - your `package.json` file and add the following script: - - ```jsonc - // package.json - - { - "scripts": { - "test": "xss-scan" - } - } - ``` - -4. Ensure that your code editor is using the TypeScript version from your project's - `node_modules` instead of the globally installed TypeScript. For Visual Studio Code, - you can configure this in your workspace settings: - - ```jsonc - // .vscode/settings.json - - { - "typescript.tsdk": "node_modules/typescript/lib" - } - ``` - -
-
-
- -> [!CAUTION] -> -> # Be sure your setup is working correclty! -> -> Try writing `console.log(
{String.name}
);` in your editor. If it **THROWS** a -> `XSS` error, then your setup is correct. Refer to the -> [@kitajs/ts-html-plugin](https://github.com/kitajs/ts-html-plugin) repository for more -> details on setting up editor intellisense. _(It should throw, as `String.name` has a -> type of `string`, type which may or may not have special caracters)_ - -
-
-
- -## Getting Started - -After successfully installing and configuring your project, you can start using JSX syntax -to generate HTML. Here are two options for importing the `@kitajs/html` package: - -1. **Import it manually**: Import the package in your TypeScript files where you need it, - avoiding global scope pollution. - - ```tsx - // my-file.tsx - - console.log(
Html import needs to be in scope!
); - ``` - -2. **Use the register to add a global namespace**: Import the `register` to globally - register all necessary functions for convenience. - - ```tsx - // Import the register to globally register all needed functions - import '@kitajs/html/register'; - - // another-file.tsx - console.log(
It works without importing!
); - ``` - -Now you can use JSX to generate HTML throughout your project. Always use the `safe` -attribute or manually call `Html.escapeHtml` to protect against XSS vulnerabilities when -rendering user input. - -Ensuring XSS prevention is vital to guarantee your application's security. You can employ -the [`@kitajs/ts-html-plugin`](https://github.com/kitajs/ts-html-plugin) to catch XSS -issues in your code editor and enhance your code's security. - -
- -## Sanitization - -
- -> [!IMPORTANT] -> Please utilize our `@kitajs/ts-html-plugin` to emit TypeScript errors wherever you are -> exposed to XSS. Refer to [Getting Started](#getting-started) for installation -> instructions. - -
- -This package sanitizes every attribute by default. However, since the resulting element is -always a string, it's impossible to differentiate an HTML element created by a `` or -from user input. This necessitates the use of the provided [`safe`](#the-safe-attribute) -or manual invocation of `Html.escapeHtml`. Or you can also use the `Html.escape` or its -alias `e` template function. - -```tsx -
⚠️ This will NOT be escaped and WILL expose you to XSS
- -
-
This WILL be escaped
-
{Html.escapeHtml('This WILL be escaped')}
-``` - -or using the `Html.escape` or its alias `e` template function: - -```tsx -import { e } from '@kitajs/html'; - -
⚠️ This will NOT be escaped and WILL expose you to XSS
- -
-
This WILL be escaped
-
{Html.escape`This WILL be escaped`}
-
{e`This WILL be escaped`}
-``` - -Here's an example of how this is **DANGEROUS** for your application: - -```tsx -user = { - name: 'Bad guy', - description: '' -} - -
{user.description}
-// Renders this HTML, which will execute malicious code: -
- -
- -
{user.description}
-// Renders this safe HTML, which will NOT execute any malicious code: -
- </div><script>getStoredPasswordAndSentToBadGuysServer()</script> -
-``` - -
- -### The Safe Attribute - -Always use the `safe` attribute when rendering uncontrolled user input. This will sanitize -the contents and prevent XSS attacks. - -```tsx -function UserCard({ name, description, date, about }) { - return ( -
-

{name}

-
-

{description}

-
- // Controlled input, no need to sanitize - -
-

{about}

-
- ); -} -``` - -Note that you should only use the `safe` attribute at the very bottom of the HTML tree -where it's needed. - -
- -## Editor Intellisense and CLI tool - -

- Example of an error thrown by @kitajs/ts-html-plugin. -

- -

⚠️

- -**Note:** This section has been relocated to the -[@kitajs/ts-html-plugin](https://github.com/kitajs/ts-html-plugin) repository. - -Please consult their -"[Getting Started](https://github.com/kitajs/ts-html-plugin#getting-started)" section for -instructions on enabling editor IntelliSense and using the CLI tool. - -
-
-
-
- -## Async Components - -Async components are supported. When any child or sub child of a component tree is a -promise, the whole tree will return a promise of the html string. - -If no async components are found, the result will be simply a string, and you can safely -cast it into a string. - -```tsx -async function Async() { - await callApi(); - return
Async!
; -} - -function Sync() { - return
Sync!
; -} - -const async = ( -
- -
-); - -async instanceof Promise; - -const sync: string = ( -
- -
-); - -typeof sync === 'string'; -``` - -A `JSX.Element` will always be a string. Once a children element is a async component, the -entire upper tree will also be async. -[Learn when JSX.Element is a Promise](#why-jsxelement-is-a-promise). - -
- -### Suspense component - -The only problem when rendering templates is that you must wait for the whole template to -be rendered before sending it to the client. This is not a problem for small templates, -but it can be a problem for large templates. - -To solve this problem, we provide a `Suspense` component that combined with -`renderToStream()` rendering method, will stream a fallback component while it waits for -his children to be rendered. This is a perfect combo to use with -[async components](#async-components). - -```tsx -import { Suspense, renderToStream } from '@kitajs/html/suspense'; - -async function MyAsyncComponent() { - const data = await database.query(); - return ; -} - -function renderUserPage(rid: number) { - return ( - Loading username...} - catch={(err) =>
Error: {err.stack}
} - > - -
- ); -} - -// Html is a string readable stream that can be piped to the client -const html = renderToStream(renderUserPage); -``` - -
- -> [!NOTE] -> The `renderToStream()` is returns a native node/bun stream, head over to our -> [suspense-server](examples/suspense-server.tsx) example to see how to use it with -> node:http, Express or Fastify servers. - -
- -The above example would render `
Loading username...
` while waiting for the -`MyAsyncComponent` to be rendered. - -When using `Suspense`, you cannot just call the component and get the html string, you -need to use the `renderToStream` function to get a stream that can be piped to the client -with updates. Otherwise, the fallback would render forever. - -As the result of any JSX component is always a string, you must use the `rid` provided by -`renderToStream` into all your suspense components, this way we can identify which -suspense is for which request and be able to render concurrent requests. - -Suspense also accepts async fallbacks, but it blocks rendering until the fallback is -resolved. - -```tsx -function renderTemplate(rid: number) { - return ( - } - catch={(err) =>
Error: {err.stack}
} - > - -
- ); -} -``` - -The above example would only return anything after `MyAsyncFallback` is resolved. To catch -async fallback errors, you must wrap it into a [`ErrorBoundary`](#error-boundaries). - -
- -### Error boundaries - -The same way as promises must be awaited to resolve its own html, errors must be caught. -Outside of suspense components, you can use the provided error boundaries to catch errors. - -```tsx -import { ErrorBoundary } from '@kitajs/html/error-boundary'; - -async function MyAsyncComponent() { - const data = await database.query(); // this promise may reject - return ; -} - -function renderTemplate() { - return ( -
Error: {err.stack}
}> - -
- ); -} - -// If MyAsyncComponent throws an error, it will render
Error: ...
-const html = await renderTemplate(); -``` - -Error boundaries will only work for errors thrown inside async components, for sync -components you must use try/catch. - -```tsx -function MySyncComponent() { - try { - const data = database.query(); // this may throw an error - return ; - } catch (err) { - return
Error: {err.stack}
; - } -} -``` - -Error boundaries outside suspense components will only catch errors thrown by the fallback -component. You must use the Suspense's `catch` property to handle errors thrown by its -children components. - -```tsx -function renderTemplate(rid: number) { - return ( - fallback error}> - } - catch={
Children error
} - > - -
-
- ); -} - -const html = renderToStream(renderTemplate); -``` - -The above example would render `
Children error
` if `MyAsyncComponent` throws an -error, or `
fallback error
` if `MyAsyncFallback` throws an error. If both throws -an error, the first error will be changed to the second error as soon as the children -error is thrown. - -
- -### Why JSX.Element is a Promise? - -
- -> [!NOTE] -> Until [#14729](https://github.com/microsoft/TypeScript/issues/14729) gets implemented, -> you need to manually cast `JSX.Element` into strings if you are sure there is no inner -> async components in your component tree. - -
- -JSX elements are mostly strings everywhere. However, as the nature of this package, once a -children element is a async component, the entire upper tree will also be async. Unless -you are sure that no other component in your entire codebase is async, you should always -handle both string and promise cases. - -```tsx -// It may or may not have inner async components. -const html = ; - -if (html instanceof Promise) { - // I'm a promise, I should be awaited - console.log(await html); -} else { - // I'm a string, I can be used as is - console.log(html); -} -``` +

+ KitaJS Html Monorepo +


-### Why there is no `context` API? +## Packages -We choose to not provide a `context` API. Here the reasons why: +- [@kitajs/html](./packages/html) +- [@kitajs/ts-html-plugin](./packages/ts-html-plugin) +- [@kitajs/fastify-html-plugin](./packages/fastify-html-plugin) -This library only outputs strings (or Promises that resolve to strings), we don't manage -lifecycle or state. So the main selling point to having a `context` API is to avoid -[prop drilling](https://www.geeksforgeeks.org/what-is-prop-drilling-and-how-to-avoid-it). +## Performance & Benchmarks -A context Api would need to use a request identifier (internally called `rid`) to be safe -to access asynchronously. Take for example two requests that are being processed at the -same time, both of them use some async components. If we use a global context without -scoping it behind some sort of `rid`, the second request would override the context of the -first request. So for the first request, some part of the rendering (the non async part) -would use the correct context, but then the other section inside the async component would -use the context of the second request. **This is bad.**. Each component that uses context -would need to receive the `rid` as a prop. This will defeat the purpose of having a -context in the first place. The `rid` is needed to make the context async safe. - -The only way to maintain data consistency across concurrent renders without attaching a -request locator (`rid`), is by using -[ALS](https://nodejs.org/api/async_context.html#class-asynclocalstorage). However, this -approach introduces a lot of overhead and a significant performance penalty. - -Our recommendation is to use props. If you want to avoid prop drilling, you can use a -composition style of writing components. This is a common pattern in other JSX-based -libraries, and it works well with this library too. - -```tsx -// Drills only the required properties. -app.get('/', (request, response) => ( - - - - - - - -)); -``` - -
- -## Migrating from HTML - -Migrating from plain HTML to JSX can be a pain to convert it all manually, as you will -find yourself hand placing quotes and closing void elements. - -You can use [**Html To Jsx**](https://magic.reactjs.net/htmltojsx.htm). - -```html - -
- - -
-

Enter your HTML here

-``` - -Results into: - -```tsx -<> - {/* Hello world */} -
- - -
-

Enter your HTML here

- -``` - -
- -### Htmx - -The usage of [htmx.org](https://htmx.org/) is super common with this project, this is why -we also provide type definitions for all HTMX attributes. - -You just need to add this triple slash directive to the top of your file: - -```tsx -/// - -const html = ( - // Type checking and intellisense for all HTMX attributes -
- Click me! -
-); -``` - -
- -### Alpinejs - -[Alpinejs](https://alpinejs.dev/) is commonly used with htmx. - -You just need to add this triple slash directive to the top of your file: - -```tsx -/// - -const html = ( - // Type checking and intellisense for all HTMX attributes -
...
-); -``` - -
- -### Hotwire Turbo - -This project supports the usage of [Turbo Hotwire](https://turbo.hotwired.dev/). We -provide a separate export that you can use to provide type definitions for the elements -and attributes used with Turbo Hotwire. - -You just need to add this triple slash directive to the top of your file: - -```tsx -/// - -const html = ( - // Type checking and intellisense for all HTMX attributes - - Show all expanded messages in this frame. - -
Show response from this form within this frame.
-
-); -``` - -
- -### Base HTML templates - -Often you will have a "template" html with doctype, things on the head, body and so on... -Most users try to use them as a raw string and only use JSX for other components, but this -is a not a good idea as -[you will have problems with it](https://github.com/nicojs/typed-html/issues/46). - -But you can always concatenate strings, like in this required use-case for `` - -```tsx -export function Layout(props: Html.PropsWithChildren<{ head: string; title?: string }>) { - return ( - <> - {''} - - - - - {props.title || 'Hello World!'} - {props.head} - - {props.children} - - - ); -} - -const html = ( - - - + + + + + + + + + + + + ); +} + +function Header({ name }) { + return ( +
+

Hello {name}

+ +
+ ); +} + +function Footer({ name }) { + return ( +
+ + + +
+ ); +} + +function Main({ children, name }) { + return ( +
+
+
{children}
+
+
+ ); +} + +function UserProfile({ name }) { + return ( + + ); +} + +function Sidebar() { + return ( + + ); +} + +function PageContent() { + return ( +
+

Welcome to our store

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla venenatis magna id + dolor ultricies, eget pretium ligula sodales. Cras sit amet turpis nec lacus + blandit placerat. Sed vestibulum est sit amet enim ultrices rutrum. Vivamus in + nulla vel nunc interdum vehicula. +

+

+ Pellentesque efficitur tellus id velit vehicula laoreet. Proin et neque ac dolor + hendrerit elementum. Fusce auctor metus non ligula tincidunt, id gravida odio + sollicitudin. +

+
+ ); +} + +export function RealWorldPage(name: string) { + return ( + }> +
+

Purchases

+ +
+ {purchases.map((purchase) => ( + + ))} +
+ + + + +
+
+ ); +} diff --git a/benchmarks/kitajs/tsconfig.json b/benchmarks/kitajs/tsconfig.json new file mode 100644 index 000000000..3d1b7c1c0 --- /dev/null +++ b/benchmarks/kitajs/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "module": "Node16", + "target": "ESNext", + "jsx": "react-jsx", + "moduleResolution": "Node16", + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "jsxImportSource": "@kitajs/html", + "declaration": true, + "declarationMap": true + }, + "include": ["renderers", "index.ts"], + "exclude": ["dist"] +} diff --git a/benchmarks/reactjsx/index.ts b/benchmarks/reactjsx/index.ts new file mode 100644 index 000000000..0177d0ca3 --- /dev/null +++ b/benchmarks/reactjsx/index.ts @@ -0,0 +1,3 @@ +export * from './renderers/many-components'; +export * from './renderers/many-props'; +export * from './renderers/real-world'; diff --git a/benchmarks/reactjsx/package.json b/benchmarks/reactjsx/package.json new file mode 100644 index 000000000..1b2951aa8 --- /dev/null +++ b/benchmarks/reactjsx/package.json @@ -0,0 +1,17 @@ +{ + "name": "@kitajs/bench-html-reactjsx", + "private": true, + "main": "dist/index.js", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "react": "^18.2.0", + "tslib": "^2.6.2", + "typescript": "^5.3.3" + }, + "devDependencies": { + "@types/node": "^20.11.20", + "@types/react": "^18.2.60" + } +} diff --git a/benchmarks/reactjsx/renderers/many-components.tsx b/benchmarks/reactjsx/renderers/many-components.tsx new file mode 100644 index 000000000..a9a28e5b0 --- /dev/null +++ b/benchmarks/reactjsx/renderers/many-components.tsx @@ -0,0 +1,7 @@ +export function ManyComponents(name: string) { + for (let i = 0; i < 10000; i++) { +
{name}
; + } + + return
{name}
; +} diff --git a/benchmarks/reactjsx/renderers/many-props.tsx b/benchmarks/reactjsx/renderers/many-props.tsx new file mode 100644 index 000000000..bb625f2fd --- /dev/null +++ b/benchmarks/reactjsx/renderers/many-props.tsx @@ -0,0 +1,216 @@ +export function ManyProps(name: string) { + return ( + + ); +} diff --git a/benchmarks/reactjsx/renderers/real-world.tsx b/benchmarks/reactjsx/renderers/real-world.tsx new file mode 100644 index 000000000..2d18c234f --- /dev/null +++ b/benchmarks/reactjsx/renderers/real-world.tsx @@ -0,0 +1,164 @@ +const purchases = Array.from({ length: 10000 }, (_, i) => ({ + id: i + 1, + name: `Item ${i + 1}`, + price: Math.random() * 10, + quantity: Math.floor(Math.random() * 10) + 1 +})); + +function Purchase({ id, name, price, quantity }) { + return ( +
+
{name}
+
{price}
+
{quantity}
+
+ ); +} + +function Layout({ children, head }) { + return ( + + {head} + {children} + + ); +} + +function Head({ title }) { + return ( +
+ {title} + + + + + + + + + + + + + + + + +
+ ); +} + +function Header({ name }) { + return ( +
+

Hello {name}

+ +
+ ); +} + +function Footer({ name }) { + return ( +
+

+ © {new Date().getFullYear()} {name} +

+ +

+ Terms + Privacy +

+
+ ); +} + +function Main({ children, name }) { + return ( +
+
+
{children}
+
+
+ ); +} + +function UserProfile({ name }) { + return ( +
+

User Profile

+

Name: {name}

+

Email: example@example.com

+

Address: 123 Main St, City, Country

+

Phone: 123-456-7890

+
+ ); +} + +function Sidebar() { + return ( + + ); +} + +function PageContent() { + return ( +
+

Welcome to our store

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla venenatis magna id + dolor ultricies, eget pretium ligula sodales. Cras sit amet turpis nec lacus + blandit placerat. Sed vestibulum est sit amet enim ultrices rutrum. Vivamus in + nulla vel nunc interdum vehicula. +

+

+ Pellentesque efficitur tellus id velit vehicula laoreet. Proin et neque ac dolor + hendrerit elementum. Fusce auctor metus non ligula tincidunt, id gravida odio + sollicitudin. +

+
+ ); +} + +export function RealWorldPage(name: string) { + return ( + }> +
+

Purchases

+ +
+ {purchases.map((purchase, index) => ( + + ))} +
+ + + + +
+
+ ); +} diff --git a/benchmarks/reactjsx/tsconfig.json b/benchmarks/reactjsx/tsconfig.json new file mode 100644 index 000000000..a849aaf07 --- /dev/null +++ b/benchmarks/reactjsx/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "jsx": "react-jsx", + "declaration": true, + "declarationMap": true + }, + "include": ["renderers", "index.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/benchmarks/runner/assertions.js b/benchmarks/runner/assertions.js new file mode 100644 index 000000000..e620f2deb --- /dev/null +++ b/benchmarks/runner/assertions.js @@ -0,0 +1,51 @@ +import KitaHtmlJSXRuntimeRenderers from '@kitajs/bench-html-kitajs'; +import ReactJSXRuntimeRenderers from '@kitajs/bench-html-reactjsx'; +import StringTemplateRenderers from '@kitajs/bench-html-templates'; +import TypedHtmlRenderers from '@kitajs/bench-html-typed-html'; + +import CommonTags from 'common-tags'; +import * as gHtml from 'ghtml'; +import ReactDOMServer from 'react-dom/server'; + +import assert from 'node:assert'; + +export function assertHtml() { + // ENSURE THAT ALL RENDERERS PRODUCE THE SAME OUTPUT AGAINST KITAJS/HTML + // Ensures that Kitajs/html and react produce the same output + assert.equal( + KitaHtmlJSXRuntimeRenderers.ManyComponents('Hello World!'), + ReactDOMServer.renderToStaticMarkup( + ReactJSXRuntimeRenderers.ManyComponents('Hello World!') + ) + ); + + // Ensures that Kitajs/html and common-tags produce the same output + assert.equal( + KitaHtmlJSXRuntimeRenderers.ManyComponents('Hello World!'), + // Simply removes spaces and newlines + StringTemplateRenderers.TemplateManyComponents(CommonTags.html, 'Hello World!') + .split('\n') + .map((l) => l.trim()) + .join('') + ); + + // Ensures that Kitajs/html and ghtml produce the same output + assert.equal( + KitaHtmlJSXRuntimeRenderers.ManyComponents('Hello World!'), + // Simply removes spaces and newlines + StringTemplateRenderers.TemplateManyComponents(gHtml.html, 'Hello World!') + .split('\n') + .map((l) => l.trim()) + .join('') + ); + + // Kitajs/html and typed html does produces the same output, however typed-html appends spaces between tags + assert.equal( + KitaHtmlJSXRuntimeRenderers.ManyComponents('Hello World!'), + // Simply removes spaces and newlines + TypedHtmlRenderers.ManyComponents('Hello World!') + .toString() + .replace(/< \//g, ' { + bench('KitaJS/Html', () => KitaHtmlJSXRuntimeRenderers.ManyComponents('Hello World!')); + bench('Typed Html', () => TypedHtmlRenderers.ManyComponents('Hello World!')); + bench('React', () => + ReactDOMServer.renderToStaticMarkup( + ReactJSXRuntimeRenderers.ManyComponents('Hello World!') + ) + ); + bench('Common Tags', () => + StringTemplateRenderers.TemplateManyComponents(CommonTags.html, 'Hello World!') + ); + bench('Ghtml', () => + StringTemplateRenderers.TemplateManyComponents(gHtml.html, 'Hello World!') + ); +}); + +group('Many Props (7.4kb)', () => { + bench('KitaJS/Html', () => KitaHtmlJSXRuntimeRenderers.ManyProps('Hello World!')); + bench('Typed Html', () => TypedHtmlRenderers.ManyProps('Hello World!')); + bench('React', () => + ReactDOMServer.renderToStaticMarkup( + ReactJSXRuntimeRenderers.ManyProps('Hello World!') + ) + ); + bench('Common Tags', () => + StringTemplateRenderers.TemplateManyProps(CommonTags.html, 'Hello World!') + ); + bench('Ghtml', () => + StringTemplateRenderers.TemplateManyProps(gHtml.html, 'Hello World!') + ); +}); + +group('MdnHomepage (66.7Kb)', () => { + bench('KitaJS/Html', () => KitaHtmlJSXRuntimeRenderers.RealWorldPage('Hello World!')); + bench('Typed Html', () => TypedHtmlRenderers.RealWorldPage('Hello World!')); + bench('React', () => + ReactDOMServer.renderToStaticMarkup( + ReactJSXRuntimeRenderers.RealWorldPage('Hello World!') + ) + ); + bench('Common Tags', () => + StringTemplateRenderers.RealWorldPage(CommonTags.html, 'Hello World!') + ); + bench('Ghtml', () => StringTemplateRenderers.RealWorldPage(gHtml.html, 'Hello World!')); +}); + +run().catch(console.error); diff --git a/benchmarks/runner/package.json b/benchmarks/runner/package.json new file mode 100644 index 000000000..749cbcc81 --- /dev/null +++ b/benchmarks/runner/package.json @@ -0,0 +1,17 @@ +{ + "name": "@kitajs/bench-html-runner", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "@kitajs/bench-html-kitajs": "workspace:*", + "@kitajs/bench-html-reactjsx": "workspace:*", + "@kitajs/bench-html-templates": "workspace:*", + "@kitajs/bench-html-typed-html": "workspace:*", + "common-tags": "^1.8.2", + "ghtml": "^1.0.1", + "mitata": "^0.1.11", + "react-dom": "^18.2.0" + } +} diff --git a/benchmarks/templates/index.ts b/benchmarks/templates/index.ts new file mode 100644 index 000000000..0177d0ca3 --- /dev/null +++ b/benchmarks/templates/index.ts @@ -0,0 +1,3 @@ +export * from './renderers/many-components'; +export * from './renderers/many-props'; +export * from './renderers/real-world'; diff --git a/benchmarks/templates/package.json b/benchmarks/templates/package.json new file mode 100644 index 000000000..ea8dbce36 --- /dev/null +++ b/benchmarks/templates/package.json @@ -0,0 +1,15 @@ +{ + "name": "@kitajs/bench-html-templates", + "private": true, + "main": "dist/index.js", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "tslib": "^2.6.2", + "typescript": "^5.3.3" + }, + "devDependencies": { + "@types/node": "^20.11.20" + } +} diff --git a/benchmarks/templates/renderers/many-components.tsx b/benchmarks/templates/renderers/many-components.tsx new file mode 100644 index 000000000..33a49d3ba --- /dev/null +++ b/benchmarks/templates/renderers/many-components.tsx @@ -0,0 +1,7 @@ +export function TemplateManyComponents(html: Function, name: string) { + for (let i = 0; i < 10000; i++) { + html`
${name}
`; + } + + return html`
${name}
`; +} diff --git a/benchmarks/templates/renderers/many-props.tsx b/benchmarks/templates/renderers/many-props.tsx new file mode 100644 index 000000000..3f8fcec94 --- /dev/null +++ b/benchmarks/templates/renderers/many-props.tsx @@ -0,0 +1,234 @@ +export function TemplateManyProps(html: Function, name: string) { + return html``; +} diff --git a/benchmarks/templates/renderers/real-world.tsx b/benchmarks/templates/renderers/real-world.tsx new file mode 100644 index 000000000..d28eacce1 --- /dev/null +++ b/benchmarks/templates/renderers/real-world.tsx @@ -0,0 +1,169 @@ +const purchases = Array.from({ length: 10000 }, (_, i) => ({ + id: i + 1, + name: `Item ${i + 1}`, + price: Math.random() * 10, + quantity: Math.floor(Math.random() * 10) + 1 +})); + +function Purchase(html: Function, { id, name, price, quantity }) { + return html` +
+
${name}
+
${price}
+
${quantity}
+
+ `; +} + +function Layout(html: Function, { children, head }: any) { + return html` + + + !${head} + + + !${children} + + + `; +} + +function Head(html: Function, { title }) { + return html` +
+ ${title} + + + + + + + + + + + + + + + + +
+ `; +} + +function Header(html: Function, { name }) { + return html` +
+

Hello ${name}

+ +
+ `; +} + +function Footer(html: Function, { name }) { + return html` +
+ + + +
+ `; +} + +function Main(html: Function, { children, name }: any) { + return html` +
+ !${Header(html, { name })} +
!${children}
+ !${Footer(html, { name })} +
+ `; +} + +function UserProfile(html: Function, { name }) { + return html` + + `; +} + +function Sidebar(html: Function) { + return html` + + `; +} + +function PageContent(html: Function) { + return html` +
+

Welcome to our store

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla venenatis magna id + dolor ultricies, eget pretium ligula sodales. Cras sit amet turpis nec lacus + blandit placerat. Sed vestibulum est sit amet enim ultrices rutrum. Vivamus in + nulla vel nunc interdum vehicula. +

+

+ Pellentesque efficitur tellus id velit vehicula laoreet. Proin et neque ac dolor + hendrerit elementum. Fusce auctor metus non ligula tincidunt, id gravida odio + sollicitudin. +

+
+ `; +} + +export function RealWorldPage(html: Function, name: string) { + return html`${Layout(html, { + head: Head(html, { title: 'Real World Example' }), + children: Main(html, { + name, + children: [ + html`

Purchases

`, + html`
+ ${purchases.map((purchase) => + Purchase(html, { + id: purchase.id, + name: purchase.name, + price: purchase.price, + quantity: purchase.quantity + }) + )} +
`, + UserProfile(html, { name }), + Sidebar(html), + PageContent(html) + ] + }) + })}`; +} diff --git a/benchmarks/templates/tsconfig.json b/benchmarks/templates/tsconfig.json new file mode 100644 index 000000000..b45614e11 --- /dev/null +++ b/benchmarks/templates/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "module": "Node16", + "target": "ESNext", + "jsx": "react-jsx", + "jsxImportSource": "@kitajs/html", + "declaration": true, + "declarationMap": true + }, + "include": ["renderers", "index.ts"], + "exclude": ["dist"] +} diff --git a/benchmarks/typed-html/index.ts b/benchmarks/typed-html/index.ts new file mode 100644 index 000000000..0177d0ca3 --- /dev/null +++ b/benchmarks/typed-html/index.ts @@ -0,0 +1,3 @@ +export * from './renderers/many-components'; +export * from './renderers/many-props'; +export * from './renderers/real-world'; diff --git a/benchmarks/typed-html/package.json b/benchmarks/typed-html/package.json new file mode 100644 index 000000000..9a1938fe5 --- /dev/null +++ b/benchmarks/typed-html/package.json @@ -0,0 +1,13 @@ +{ + "name": "@kitajs/bench-html-typed-html", + "private": true, + "main": "dist/index.js", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "tslib": "^2.6.2", + "typed-html": "^3.0.1", + "typescript": "^5.3.3" + } +} diff --git a/benchmarks/typed-html/renderers/many-components.tsx b/benchmarks/typed-html/renderers/many-components.tsx new file mode 100644 index 000000000..a0c6f0c99 --- /dev/null +++ b/benchmarks/typed-html/renderers/many-components.tsx @@ -0,0 +1,9 @@ +import * as elements from 'typed-html'; + +export function ManyComponents(name: string) { + for (let i = 0; i < 10000; i++) { +
{name}
; + } + + return
{name}
; +} diff --git a/benchmark/renderers/many-props.tsx b/benchmarks/typed-html/renderers/many-props.tsx similarity index 50% rename from benchmark/renderers/many-props.tsx rename to benchmarks/typed-html/renderers/many-props.tsx index 6556e909a..8c21eb26f 100644 --- a/benchmark/renderers/many-props.tsx +++ b/benchmarks/typed-html/renderers/many-props.tsx @@ -1,4 +1,7 @@ -export function ManyProps(Html: { createElement: Function }, name: string) { +//@ts-nocheck +import * as elements from 'typed-html'; + +export function ManyProps(name: string) { return (
); } - -export function TemplateManyProps(html: Function, name: string) { - return html``; -} diff --git a/benchmarks/typed-html/renderers/real-world.tsx b/benchmarks/typed-html/renderers/real-world.tsx new file mode 100644 index 000000000..83b987cb3 --- /dev/null +++ b/benchmarks/typed-html/renderers/real-world.tsx @@ -0,0 +1,165 @@ +import * as elements from 'typed-html'; + +const purchases = Array.from({ length: 10000 }, (_, i) => ({ + id: i + 1, + name: `Item ${i + 1}`, + price: Math.random() * 10, + quantity: Math.floor(Math.random() * 10) + 1 +})); + +function Purchase({ id, name, price, quantity }) { + return ( +
+
{name}
+
{price}
+
{quantity}
+
+ ); +} + +function Layout({ children, head }: any) { + return ( + + {head} + {children} + + ); +} + +function Head({ title }) { + return ( +
+ {title} + + + + + + + + + + + + + + + + +
+ ); +} + +function Header({ name }) { + return ( +
+

Hello {name}

+ +
+ ); +} + +function Footer({ name }) { + return ( +
+ + + +
+ ); +} + +function Main({ children, name }: any) { + return ( +
+
+
{children}
+
+
+ ); +} + +function UserProfile({ name }) { + return ( + + ); +} + +function Sidebar() { + return ( + + ); +} + +function PageContent() { + return ( +
+

Welcome to our store

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla venenatis magna id + dolor ultricies, eget pretium ligula sodales. Cras sit amet turpis nec lacus + blandit placerat. Sed vestibulum est sit amet enim ultrices rutrum. Vivamus in + nulla vel nunc interdum vehicula. +

+

+ Pellentesque efficitur tellus id velit vehicula laoreet. Proin et neque ac dolor + hendrerit elementum. Fusce auctor metus non ligula tincidunt, id gravida odio + sollicitudin. +

+
+ ); +} + +export function RealWorldPage(name: string) { + return ( + }> +
+

Purchases

+ +
+ {purchases.map((purchase) => ( + + ))} +
+ + + + +
+
+ ); +} diff --git a/benchmarks/typed-html/tsconfig.json b/benchmarks/typed-html/tsconfig.json new file mode 100644 index 000000000..18843578b --- /dev/null +++ b/benchmarks/typed-html/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "module": "Node16", + "moduleResolution": "Node16", + "target": "ESNext", + "jsx": "react", + "jsxFactory": "elements.createElement", + "declaration": true, + "declarationMap": true + }, + "include": ["renderers", "index.ts"], + "exclude": ["dist"] +} diff --git a/examples/suspense-server.tsx b/examples/suspense-server.tsx index ae481f5f4..ae97d56db 100644 --- a/examples/suspense-server.tsx +++ b/examples/suspense-server.tsx @@ -1,7 +1,7 @@ import http from 'http'; import { setTimeout } from 'timers/promises'; import Html, { PropsWithChildren } from '../index'; -import { Suspense, renderToStream } from '../suspense'; +import { Suspense, renderToStream } from '../packages/html/suspense'; async function SleepForMs({ ms, children }: PropsWithChildren<{ ms: number }>) { await setTimeout(ms * 2); diff --git a/package.json b/package.json index c9b1f8352..577b19a93 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,45 @@ { - "name": "@kitajs/html", - "version": "3.1.2", - "description": "Fast and type safe HTML templates using TypeScript.", - "bugs": "https://github.com/kitajs/html/issues", + "name": "@kitajs/html-monorepo", + "version": "1.0.0", + "private": true, + "homepage": "https://kita.js.org", + "bugs": { + "url": "https://github.com/kitajs/html/issues" + }, "repository": { "type": "git", "url": "git+https://github.com/kitajs/html.git" }, - "funding": "https://github.com/kitajs/html?sponsor=1", - "license": "Apache-2.0", - "author": "arthurfiorette ", - "sideEffects": [ - "register.js" + "license": "MIT", + "author": "Arthur Fiorette ", + "workspaces": [ + "packages/*", + "benchmarks/*" ], - "main": "index.js", - "types": "index.d.ts", "scripts": { - "bench": "tsc -p tsconfig.benchmark.json && cp benchmark/package.json dist/benchmark/package.json && cd dist/benchmark && node index.js", + "bench": "pnpm -r --filter \"@kitajs/bench-html-*\" build && pnpm --filter \"@kitajs/bench-html-runner\" start", + "build": "pnpm -r --aggregate-output build", + "change": "changeset", + "ci:publish": "pnpm publish -r --access public && changeset tag", + "ci:version": "changeset version && pnpm install --no-frozen-lockfile", "format": "prettier --write .", - "test": "tsc; c8 --reporter lcov --reporter text node --trace-warnings --enable-source-maps --test dist/test", - "test:only": "tsc; node --trace-warnings --test-only --test dist/test" - }, - "dependencies": { - "csstype": "^3.1.3" + "preinstall": "npx only-allow pnpm", + "prepare": "husky", + "test": "pnpm -r --no-bail --aggregate-output --parallel test", + "watch": "pnpm -r --parallel --no-bail build --watch" }, "devDependencies": { "@arthurfiorette/prettier-config": "^1.0.12", - "@types/jsdom": "^21.1.6", - "@types/node": "^20.11.5", - "benny": "^3.7.1", - "c8": "^9.1.0", - "common-tags": "^1.8.2", - "ghtml": "^1.0.1", - "jsdom": "^24.0.0", - "mitata": "^0.1.6", - "prettier": "^3.2.4", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "tslib": "^2.6.2", - "typed-html": "^3.0.1", + "@changesets/changelog-github": "^0.5.0", + "@changesets/cli": "^2.27.1", + "@kitajs/html": "workspace:*", + "husky": "^9.0.11", + "prettier": "^3.2.5", "typescript": "^5.3.3" }, - "peerDependencies": { - "@kitajs/ts-html-plugin": ">=1.3.3" - }, - "packageManager": "pnpm@8.7.5", + "packageManager": "pnpm@8.14.0", "engines": { - "node": ">=12" + "node": ">=20", + "pnpm": ">=8.8.0" } } diff --git a/packages/fastify-html-plugin/LICENSE b/packages/fastify-html-plugin/LICENSE new file mode 100644 index 000000000..6adf8788a --- /dev/null +++ b/packages/fastify-html-plugin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Kita + +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. \ No newline at end of file diff --git a/packages/fastify-html-plugin/README.md b/packages/fastify-html-plugin/README.md new file mode 100644 index 000000000..154ed74bf --- /dev/null +++ b/packages/fastify-html-plugin/README.md @@ -0,0 +1,211 @@ +

+ Using this package? Please consider donating to support my open source work ❤️ +
+ + Help @kitajs/fastify-html-plugin grow! Star and share this amazing repository with your friends and co-workers! + +

+ +
+ +

+ + Kita JS logo + +

+ +
+ +
+ License + Downloads + Bundlephobia + Last commit + Stars +
+ +
+
+ +

🖨️ Fastify KitaJS Html Plugin

+ +

+ @kitajs/fastify-html-plugin is a fastify plugin to seamlessly integrate the KitaJS Html JSX engine into your fastify application. +
+
+

+ +
+ +- [Installing](#installing) +- [Preview](#preview) +- [Installing](#installing-1) +- [Configuration](#configuration) +- [Documentation](#documentation) +- [API](#api) + - [`reply.html()`](#replyhtml) + - [`reply.streamHtml()`](#replystreamhtml) + - [`reply.setupHtmlStream()`](#replysetuphtmlstream) +- [License](#license) + +
+
+ +## Installing + +```sh +npm install @kitajs/fastify-html-plugin +``` + +
+ +## Preview + +Example of an error thrown by this LSP plugin. + +
+ +## Installing + +> [!CAUTION] +> You **must have followed the `@kitajs/html`'s +> [Installing](https://github.com/kitajs/html#installing) guide** before continuing, +> otherwise you will be vulnerable to XSS attacks. + +```ts +import kitaHtmlPlugin from '@kitajs/fastify-html-plugin'; +import fastify from 'fastify'; + +const app = fastify(); + +app.register(kitaHtmlPlugin); +``` + +## Configuration + +Every option is well documented through their respective JSDoc comments, below are the +default options. + +| Name | Description | Default | +| ------------- | ------------------------------------------------------------------------------------------------- | --------------------------- | +| `contentType` | The value of the `Content-Type` header. | `'text/html; charset=utf8'` | +| `autoDetect` | Whether to automatically detect HTML content and set the content-type when `.html()` is not used. | `true` | +| `autoDoctype` | Whether to automatically add `` to a response starting with , if not found. | `true` | +| `isHtml` | The function used to detect if a string is a html or not when `autoDetect` is enabled. | [isHtml](./lib/is-html.js) | + +
+ +## Documentation + +`@kitajs/html` is a `JSX` -> `string` runtime, this package seamlessly integrates with +`fastify` to improve the developer experience while also providing faster implementations +for this use case. + +- Read [`@kitajs/html` documentation](https://github.com/kitajs/html) for help with + templating, and all other stuff related to ``. +- Read [`@kitajs/ts-html-plugin` documentation.](https://github.com/kitajs/ts-html-plugin) + for help setting up the **XSS** detector and IDE intellisense. + +
+ +## API + +### `reply.html()` + +**Synchronously** waits for the component tree to resolve and sends it at once to the +browser. + +This method does not support the usage of ``, please use `streamHtml` instead. + +If the HTML does not start with a doctype and `opts.autoDoctype` is enabled, it will be +added automatically. + +The correct `Content-Type` header will also be defined. + +```tsx +app.get('/', (req, reply) => + reply.html( + + +

Hello, world!

+ + + ) +); +``` + +### `reply.streamHtml()` + +Sends the html to the browser as a single stream, the entire component tree will be waited +synchronously. When using any `Suspense`, its fallback will be synchronously waited and +sent to the browser in the original stream, as its children are resolved, new pieces of +html will be sent to the browser. When all `Suspense`s pending promises are resolved, the +connection is closed normally. + +> [!NOTE] +> `request.id` must be used as the `Suspense`'s `rid` parameter + +This method hijacks the response, as the html stream is just a single continuous stream in +the http body, any changes to the status code or headers after calling this method **will +not have effect**. + +If the HTML does not start with a doctype and `opts.autoDoctype` is enabled, it will be +added automatically. The correct `Content-Type` header will also be defined. + +**Http trailers are not yet supported when using `streamHtml`** + +```tsx +app.get('/', (req, reply) => + reply.streamHtml( + Loading...
}> + + + ) +); +``` + +### `reply.setupHtmlStream()` + +This function is called internally by the `streamHtml` getter. + +> [!NOTE] +> Executing code before sending the response and after creating your html is a bad pattern +> and should be avoided! + +This function must be called **manually** at the top of the route handler when you have to +execute some code **after** your root layout and **before** the `streamHtml call. + +If `setupHtmlStream` is executed and no call to `streamHtml` happens before the request +finishes, a memory leak will be created. Make sure that `setupHtmlStream` will never be +executed without being followed by `streamHtml`. + +```tsx +app.get('/bad', (_, reply) => { + const html = ; // Error: Request data was deleted before all + // suspense components were resolved. + + // code that must be executed after the template + foo(); + + return reply.streamHtml(html); +}); + +app.get('/good', (_, reply) => { + reply.setupHtmlStream(); + + const html = ; // works! + + // code that must be executed after the template + foo(); + + return reply.streamHtml(html); +}); +``` + +
+ +## License + +Licensed under the **MIT**. See [`LICENSE`](LICENSE) for more informations. + +
diff --git a/packages/fastify-html-plugin/assets/preview.png b/packages/fastify-html-plugin/assets/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..189b1000928fcb7784f4bd594bb8cb5af9c0b15c GIT binary patch literal 59332 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 L>E0;1ZvE(A_MZ^F literal 0 HcmV?d00001 diff --git a/packages/fastify-html-plugin/examples/htmx.tsx b/packages/fastify-html-plugin/examples/htmx.tsx new file mode 100644 index 000000000..36cbd0644 --- /dev/null +++ b/packages/fastify-html-plugin/examples/htmx.tsx @@ -0,0 +1,107 @@ +/// + +import fastifyFormbody from '@fastify/formbody'; +import { Suspense } from '@kitajs/html/suspense'; +import fastify from 'fastify'; +import { setTimeout } from 'timers/promises'; +import { fastifyKitaHtml } from '..'; + +const app = fastify({ logger: true }); + +// htmx requires body parsing +app.register(fastifyFormbody); +app.register(fastifyKitaHtml, { + // defaults + autoDetect: true, + autoDoctype: true +}); + +const users: string[] = ['Arthur Fiorette']; + +app.get('/', (_req, rep) => { + rep.html( + + + Fastify & Kita & Htmx + + + ); + }); + + test('Suspense async children & fallback', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( + 1}> + + + ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + assert.strictEqual( + res.body, + <> +
+
1
+
+ + {SuspenseScript} + + + + + ); + }); + + test('Suspense async fallback sync children', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( + 1)}> +
2
+
+ ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + assert.strictEqual(res.body, '
2
'); + }); + + test('Multiple async renders cleanup', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( + 1)}> + + + ) + ); + + const promises = []; + + for (const _ of Array.from({ length: 100 })) { + promises.push( + app.inject({ method: 'GET', url: '/' }).then((res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + assert.strictEqual( + res.body, + <> +
+
1
+
+ + {SuspenseScript} + + + + + ); + }) + ); + } + + await Promise.all(promises); + }); + + test('Multiple sync renders cleanup', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( + 1)}> + + + ) + ); + + for (let i = 0; i < 10; i++) { + const res = await app.inject({ method: 'GET', url: '/' }); + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + assert.strictEqual( + res.body, + <> +
+
1
+
+ + {SuspenseScript} + + + + + ); + } + }); + + test('Multiple children', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( +
+ 1
}> + + + + 2}> + + + + 3}> + + + + ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + + assert.strictEqual( + res.body, + <> +
+
+
1
+
+
+
2
+
+
+
3
+
+
+ + {SuspenseScript} + + + + + + + + + + + ); + }); + + test('Concurrent renders', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => { + const seconds = (req.query as { seconds: number }).seconds; + res.header('seconds', seconds); + + return res.streamHtml( +
+ {Array.from({ length: seconds }, (_, i) => ( + {seconds - i} loading
}> + + + ))} + + ); + }); + + const secondsArray = [9, 4, 7]; + const results = await Promise.all( + secondsArray.map((seconds) => + app.inject({ + method: 'GET', + url: '/', + query: { seconds: seconds.toString() } + }) + ) + ); + + for (const result of results) { + // biome-ignore lint/style/noNonNullAssertion: this is a test + const seconds = +result.headers.seconds!; + + assert.strictEqual(result.statusCode, 200); + assert.strictEqual(result.headers['content-type'], CONTENT_TYPE_VALUE); + assert.strictEqual( + result.body, + <> +
+ {Array.from({ length: seconds }, (_, i) => ( +
+
{seconds - i} loading
+
+ ))} +
+ + {SuspenseScript} + + {Array.from({ length: seconds }, (_, i) => ( + <> + + + + ))} + + ); + } + }); + + test('throws if used outside of streamHtml', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (_, res) => + res.html( + + {Promise.resolve(2)} + + ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.strictEqual(res.statusCode, 500); + assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8'); + assert.deepStrictEqual(res.json(), { + statusCode: 500, + error: 'Internal Server Error', + message: 'Request data was deleted before all suspense components were resolved.' + }); + }); + + test('works with parallel deep suspense calls resolving first', async (t) => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( +
+ {Array.from({ length: 5 }, (_, i) => ( + {i} fb outer
}> +
Outer {i}!
+ + + {i} fb inner!}> + +
Inner {i}!
+
+
+
+ + ))} + + ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + + await t.test('Html stream', () => { + assert.strictEqual( + res.body, + <> +
+
+
0 fb outer
+
+
+
1 fb outer
+
+
+
2 fb outer
+
+
+
3 fb outer
+
+
+
4 fb outer
+
+
+ + {SuspenseScript} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + }); + + await t.test('Browser simulation', async () => { + // Simulates a browser + assert.equal( + new JSDOM(res.body, { runScripts: 'dangerously' }).window.document.body.innerHTML, + <> +
+
Outer 0!
+
Inner 0!
+
Outer 1!
+
Inner 1!
+
Outer 2!
+
Inner 2!
+
Outer 3!
+
Inner 3!
+
Outer 4!
+
Inner 4!
+
+ {SuspenseScript} + + ); + }); + }); + + test('tests suspense without error boundary', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + app.get('/', (req, res) => + res.streamHtml( + 1}> + {Promise.reject(new Error('component failed'))} + + ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.deepStrictEqual(res.json(), { + error: 'Internal Server Error', + message: 'component failed', + statusCode: 500 + }); + }); + + test('tests suspense with function error boundary', async () => { + await using app = fastify(); + app.register(fastifyKitaHtml); + + const err = new Error('component failed'); + + app.get('/', (req, res) => + res.streamHtml( + 1} + catch={(err2) => { + assert.equal(err2, err); + return
3
; + }} + > + {Promise.reject(err)} +
+ ) + ); + + const res = await app.inject({ method: 'GET', url: '/' }); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-type'], CONTENT_TYPE_VALUE); + assert.strictEqual( + res.body, + <> +
+
1
+
+ + {SuspenseScript} + + + + ); + }); +}); diff --git a/packages/fastify-html-plugin/tsconfig.json b/packages/fastify-html-plugin/tsconfig.json new file mode 100644 index 000000000..8a11d9612 --- /dev/null +++ b/packages/fastify-html-plugin/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2022", + "jsx": "react-jsx", + "jsxImportSource": "@kitajs/html", + "module": "CommonJS", + "moduleResolution": "Node", + "outDir": "dist", + "plugins": [{ "name": "@kitajs/ts-html-plugin" }] + }, + "include": ["lib", "test", "index.js"] +} diff --git a/packages/fastify-html-plugin/types/index.d.ts b/packages/fastify-html-plugin/types/index.d.ts new file mode 100644 index 000000000..466d14991 --- /dev/null +++ b/packages/fastify-html-plugin/types/index.d.ts @@ -0,0 +1,174 @@ +import { FastifyPluginCallback } from 'fastify'; + +declare module 'fastify' { + interface FastifyReply { + /** + * **Synchronously** waits for the component tree to resolve and sends it at once to + * the browser. + * + * This method does not support the usage of ``, please use + * {@linkcode streamHtml} instead. + * + * If the HTML does not start with a doctype and `opts.autoDoctype` is enabled, it + * will be added automatically. + * + * The correct `Content-Type` header will also be defined. + * + * @example + * + * ```tsx + * app.get('/', (req, reply) => + * reply.html( + * + * + *

Hello, world!

+ * + * + * ) + * ); + * ``` + * + * @param html The HTML to send. + * @returns The response. + */ + html(this: this, html: JSX.Element): this | Promise; + + /** + * Sends the html to the browser as a single stream, the entire component tree will be + * waited synchronously. When using any `Suspense`, its fallback will be synchronously + * waited and sent to the browser in the original stream, as its children are + * resolved, new pieces of html will be sent to the browser. When all `Suspense`s + * pending promises are resolved, the connection is closed normally. + * + * ### `request.id` must be used as the `Suspense`'s `rid` parameter + * + * This method hijacks the response, as the html stream is just a single continuous + * stream in the http body, any changes to the status code or headers after calling + * this method **will not have effect**. + * + * If the HTML does not start with a doctype and `opts.autoDoctype` is enabled, it + * will be added automatically. The correct `Content-Type` header will also be + * defined. + * + * **Http trailers are not yet supported when using `streamHtml`** + * + * @example + * + * ```tsx + * app.get('/', (req, reply) => + * reply.streamHtml( + * Loading...}> + * + * + * ) + * ); + * ``` + * + * @param html The HTML to send. + * @returns The response. + */ + streamHtml(this: this, html: JSX.Element): this; + + /** + * This function is called internally by the `streamHtml` getter. + * + * ### Executing code before sending the response and after creating your + * + * Html is a bad pattern and should be avoided! + * + * This function must be called **manually** at the top of the route handler when you + * have to execute some code **after** your root layout and **before** the `streamHtml + * call. + * + * If `setupHtmlStream` is executed and no call to `streamHtml` happens before the + * request finishes, a memory leak will be created. Make sure that `setupHtmlStream` + * will never be executed without being followed by `streamHtml`. + * + * @example + * + * ```tsx + * app.get('/bad', (_, reply) => { + * const html = // Error: Request data was deleted before all + * // suspense components were resolved. + * + * // code that must be executed after the template + * foo(); + * + * return reply.streamHtml(html); + * }) + * + * app.get('/good', (_, reply) => { + * reply.setupHtmlStream(); + * + * const html = // works! + * + * // code that must be executed after the template + * foo(); + * + * return reply.streamHtml(html); + * }) + * ``` + */ + setupHtmlStream(this: this): this; + } +} + +type FastifyKitaHtmlPlugin = FastifyPluginCallback< + NonNullable> +>; + +declare namespace fastifyKitaHtml { + /** Options for @kitajs/fastify-html-plugin plugin. */ + export interface FastifyKitaHtmlOptions { + /** + * The value of the `Content-Type` header. + * + * @default 'text/html; charset=utf8' + */ + contentType: string; + + /** + * Whether to automatically detect HTML content and set the content-type when + * `.html()` is not used. + * + * @default true + */ + autoDetect: boolean; + + /** + * Whether to automatically add `` to a response starting with , + * if not found. + * + * ```tsx + * // With autoDoctype: true you can just return the html + * app.get('/', () => ) + * + * // With autoDoctype: false you must use rep.html + * app.get('/', (req, rep) => rep.html() + * ``` + * + * @default true + */ + autoDoctype: boolean; + + /** + * The function used to detect if a string is a html or not when `autoDetect` is + * enabled. + * + * Default implementation if length is greater than 3, starts with `<` and ends with + * `>`. + * + * There's no real way to validate HTML, so this is a best guess. + * + * @see https://stackoverflow.com/q/1732348 + * @see https://stackoverflow.com/q/11229831 + */ + isHtml: (this: void, value: string) => boolean; + } + + export const fastifyKitaHtml: FastifyKitaHtmlPlugin; + + export { fastifyKitaHtml as default }; +} + +export = fastifyKitaHtml; diff --git a/packages/fastify-html-plugin/types/index.test-d.tsx b/packages/fastify-html-plugin/types/index.test-d.tsx new file mode 100644 index 000000000..9a0210704 --- /dev/null +++ b/packages/fastify-html-plugin/types/index.test-d.tsx @@ -0,0 +1,40 @@ +import { Suspense } from '@kitajs/html/suspense'; +import fastify from 'fastify'; +import { fastifyKitaHtml } from '.'; + +const app = fastify(); + +app.register(fastifyKitaHtml); + +app.register(fastifyKitaHtml, { + autoDetect: true, + autoDoctype: true, + contentType: 'text/html; charset=utf-8', + isHtml(value) { + return value.length > 0; + } +}); + +app.get('/', async (_, reply) => { + reply.html('
hello world
'); +}); + +app.get('/jsx', async (_, reply) => { + reply.html(
hello world
); +}); + +app.get('/stream', async (_, reply) => { + reply.streamHtml('
hello world
'); +}); + +app.get('/stream/jsx', async (_, reply) => { + reply.streamHtml(
hello world
); +}); + +app.get('/stream/suspense', async (request, reply) => { + reply.streamHtml( + fallback}> + {Promise.resolve(1)} + + ); +}); diff --git a/.npmignore b/packages/html/.npmignore similarity index 100% rename from .npmignore rename to packages/html/.npmignore diff --git a/packages/html/LICENSE b/packages/html/LICENSE new file mode 100644 index 000000000..d433e2760 --- /dev/null +++ b/packages/html/LICENSE @@ -0,0 +1,201 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright 2023-present Arthur Fiorette, Nico Jansen & contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/packages/html/README.md b/packages/html/README.md new file mode 100644 index 000000000..36f78183c --- /dev/null +++ b/packages/html/README.md @@ -0,0 +1,1147 @@ +

+ Using this package? Please consider donating to support my open source work ❤️ +
+ + Help @kitajs/html grow! Star and share this amazing repository with your friends and co-workers! + +

+ +
+ +

+ + Kita JS logo + +

+ +
+ +
+ License + Codecov + Downloads + Bundlephobia + Last commit + Stars +
+ +
+
+ +

🏛️ KitaJS Html

+ +

+ @kitajs/html is a super fast JSX runtime to generate HTML strings that works everywhere. +
Express? Fastify? Hono? Bun? Htmx? +

If your code works with strings, we got you covered. +
+
+

+ +
+ +- [Preview](#preview) +- [Installing](#installing) +- [Getting Started](#getting-started) +- [Sanitization](#sanitization) + - [The Safe Attribute](#the-safe-attribute) +- [Editor Intellisense and CLI tool](#editor-intellisense-and-cli-tool) +- [Async Components](#async-components) + - [Suspense component](#suspense-component) + - [Error boundaries](#error-boundaries) + - [Why JSX.Element is a Promise?](#why-jsxelement-is-a-promise) + - [Why there is no `context` API?](#why-there-is-no-context-api) +- [Alternative way to configure your tsconfig](#alternative-way-to-configure-your-tsconfig) +- [Migrating from HTML](#migrating-from-html) + - [Htmx](#htmx) + - [Alpinejs](#alpinejs) + - [Hotwire Turbo](#hotwire-turbo) + - [Base HTML templates](#base-html-templates) +- [Compiling HTML](#compiling-html) + - [Clean Components](#clean-components) +- [Fragments](#fragments) +- [Supported HTML](#supported-html) + - [The `tag` tag](#the-tag-tag) + - [Conditional classes](#conditional-classes) +- [Extending types](#extending-types) + - [Allow everything!](#allow-everything) +- [Performance](#performance) +- [How it works](#how-it-works) +- [Serialization table](#serialization-table) +- [Format HTML output](#format-html-output) +- [Deprecating global register](#deprecating-global-register) +- [Fork credits](#fork-credits) + +
+
+ +## Preview + +Example of an error thrown by this LSP plugin. + +
+ +## Installing + +To use the `@kitajs/html` package, follow these steps: + +1. Install the required npm packages, `@kitajs/html` and `@kitajs/ts-html-plugin`, to + enable editor intellisense. Open your terminal and run: + + ```sh + npm install @kitajs/html @kitajs/ts-html-plugin + ``` + +2. Configure your TypeScript project to transpile TSX/JSX into JavaScript and using our + [LSP Plugin](#editor-intellisense-and-cli-tool). Update your `tsconfig.json` file with + the following settings: + + ```jsonc + // tsconfig.json + + { + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "@kitajs/html", + "plugins": [{ "name": "@kitajs/ts-html-plugin" }] + } + } + ``` + +3. Append the + [`xss-scan`](https://github.com/kitajs/ts-html-plugin/tree/main#running-as-cli) command + into your test script. This CLI comes from @kitajs/ts-html-plugin, which catches XSS + vulnerabilities that may be introduced into your codebase, automating the xss scanning + process to run everytime you test your code, like inside your CI/CD environment. Open + your `package.json` file and add the following script: + + ```jsonc + // package.json + + { + "scripts": { + "test": "xss-scan" + } + } + ``` + +4. Ensure that your code editor is using the TypeScript version from your project's + `node_modules` instead of the globally installed TypeScript. For Visual Studio Code, + you can configure this in your workspace settings: + + ```jsonc + // .vscode/settings.json + + { + "typescript.tsdk": "node_modules/typescript/lib" + } + ``` + +
+
+
+ +> [!CAUTION] +> +> # Be sure your setup is working correclty! +> +> Try writing `console.log(
{String.name}
);` in your editor. If it **THROWS** a +> `XSS` error, then your setup is correct. Refer to the +> [@kitajs/ts-html-plugin](https://github.com/kitajs/ts-html-plugin) repository for more +> details on setting up editor intellisense. _(It should throw, as `String.name` has a +> type of `string`, type which may or may not have special caracters)_ + +
+
+
+ +## Getting Started + +After installing the `@kitajs/html` package and configuring your TypeScript project, you +should be able to use JSX to generate HTML inside your .tsx files. + +```tsx +const html = ( +
+

Hello, world!

+

Welcome to the KitaJS HTML package.

+
+); +``` + +Always use the `safe` attribute or manually call `Html.escapeHtml` to protect against XSS +vulnerabilities when rendering user input. + +Ensuring XSS prevention is vital to guarantee your application's security. You can employ +the [`@kitajs/ts-html-plugin`](https://github.com/kitajs/ts-html-plugin) to catch XSS +issues in your code editor and enhance your code's security. + +
+ +## Sanitization + +
+ +> [!IMPORTANT] +> Please utilize our `@kitajs/ts-html-plugin` to emit TypeScript errors wherever you are +> exposed to XSS. Refer to [Getting Started](#getting-started) for installation +> instructions. + +
+ +This package sanitizes every attribute by default. However, since the resulting element is +always a string, it's impossible to differentiate an HTML element created by a `` or +from user input. This necessitates the use of the provided [`safe`](#the-safe-attribute) +or manual invocation of `Html.escapeHtml`. Or you can also use the `Html.escape` or its +alias `e` template function. + +```tsx +import { e } from '@kitajs/html'; + +
⚠️ This will NOT be escaped and WILL expose you to XSS
+ +
+
This WILL be escaped
+
{Html.escapeHtml('This WILL be escaped')}
+
{e`This WILL be escaped: ${someVar}`}
+``` + +Here's an example of how this is **DANGEROUS** for your application: + +```tsx +user = { + name: 'Bad guy', + description: '' +}; + +// Executes malicious code: +input =
{user.description}
; +output = ( +
+ +
+); + +// Does not execute any malicious code: +input = ( +
+ {user.description} +
+); +output = ( +
+ </div><script>getStoredPasswordAndSentToBadGuysServer()</script> +
+); +``` + +
+ +### The Safe Attribute + +Always use the `safe` attribute when rendering uncontrolled user input. This will sanitize +the contents and prevent XSS attacks. + +```tsx +function UserCard({ name, description, date, about }) { + return ( +
+

{name}

+
+

{description}

+
+ // Controlled input, no need to sanitize + +
+

{about}

+
+ ); +} +``` + +Note that you should only use the `safe` attribute at the very bottom of the HTML tree +where it's needed. + +
+ +## Editor Intellisense and CLI tool + +

+ Example of an error thrown by @kitajs/ts-html-plugin. +

+ +

⚠️

+ +**Note:** This section has been relocated to the +[@kitajs/ts-html-plugin](https://github.com/kitajs/ts-html-plugin) repository. + +Please consult their +"[Getting Started](https://github.com/kitajs/ts-html-plugin#getting-started)" section for +instructions on enabling editor IntelliSense and using the CLI tool. + +
+
+
+
+ +## Async Components + +Async components are supported. When any child or sub child of a component tree is a +promise, the whole tree will return a promise of the html string. + +If no async components are found, the result will be simply a string, and you can safely +cast it into a string. + +```tsx +async function Async() { + await callApi(); + return
Async!
; +} + +function Sync() { + return
Sync!
; +} + +const async = ( +
+ +
+); + +async instanceof Promise; + +const sync: string = ( +
+ +
+); + +typeof sync === 'string'; +``` + +A `JSX.Element` will always be a string. Once a children element is a async component, the +entire upper tree will also be async. +[Learn when JSX.Element is a Promise](#why-jsxelement-is-a-promise). + +
+ +### Suspense component + +The only problem when rendering templates is that you must wait for the whole template to +be rendered before sending it to the client. This is not a problem for small templates, +but it can be a problem for large templates. + +To solve this problem, we provide a `Suspense` component that combined with +`renderToStream()` rendering method, will stream a fallback component while it waits for +his children to be rendered. This is a perfect combo to use with +[async components](#async-components). + +```tsx +import { Suspense, renderToStream } from '@kitajs/html/suspense'; + +async function MyAsyncComponent() { + const data = await database.query(); + return ; +} + +function renderUserPage(rid: number) { + return ( + Loading username...} + catch={(err) =>
Error: {err.stack}
} + > + +
+ ); +} + +// Html is a string readable stream that can be piped to the client +const html = renderToStream(renderUserPage); +``` + +
+ +> [!NOTE] +> The `renderToStream()` is returns a native node/bun stream, head over to our +> [suspense-server](examples/suspense-server.tsx) example to see how to use it with +> node:http, Express or Fastify servers. + +
+ +The above example would render `
Loading username...
` while waiting for the +`MyAsyncComponent` to be rendered. + +When using `Suspense`, you cannot just call the component and get the html string, you +need to use the `renderToStream` function to get a stream that can be piped to the client +with updates. Otherwise, the fallback would render forever. + +As the result of any JSX component is always a string, you must use the `rid` provided by +`renderToStream` into all your suspense components, this way we can identify which +suspense is for which request and be able to render concurrent requests. + +Suspense also accepts async fallbacks, but it blocks rendering until the fallback is +resolved. + +```tsx +function renderTemplate(rid: number) { + return ( + } + catch={(err) =>
Error: {err.stack}
} + > + +
+ ); +} +``` + +The above example would only return anything after `MyAsyncFallback` is resolved. To catch +async fallback errors, you must wrap it into a [`ErrorBoundary`](#error-boundaries). + +
+ +### Error boundaries + +The same way as promises must be awaited to resolve its own html, errors must be caught. +Outside of suspense components, you can use the provided error boundaries to catch errors. + +```tsx +import { ErrorBoundary } from '@kitajs/html/error-boundary'; + +async function MyAsyncComponent() { + const data = await database.query(); // this promise may reject + return ; +} + +function renderTemplate() { + return ( +
Error: {err.stack}
}> + +
+ ); +} + +// If MyAsyncComponent throws an error, it will render
Error: ...
+const html = await renderTemplate(); +``` + +Error boundaries will only work for errors thrown inside async components, for sync +components you must use try/catch. + +```tsx +function MySyncComponent() { + try { + const data = database.query(); // this may throw an error + return ; + } catch (err) { + return
Error: {err.stack}
; + } +} +``` + +Error boundaries outside suspense components will only catch errors thrown by the fallback +component. You must use the Suspense's `catch` property to handle errors thrown by its +children components. + +```tsx +function renderTemplate(rid: number) { + return ( + fallback error}> + } + catch={
Children error
} + > + +
+
+ ); +} + +const html = renderToStream(renderTemplate); +``` + +The above example would render `
Children error
` if `MyAsyncComponent` throws an +error, or `
fallback error
` if `MyAsyncFallback` throws an error. If both throws +an error, the first error will be changed to the second error as soon as the children +error is thrown. + +
+ +### Why JSX.Element is a Promise? + +
+ +> [!NOTE] +> Until [#14729](https://github.com/microsoft/TypeScript/issues/14729) gets implemented, +> you need to manually cast `JSX.Element` into strings if you are sure there is no inner +> async components in your component tree. + +
+ +JSX elements are mostly strings everywhere. However, as the nature of this package, once a +children element is a async component, the entire upper tree will also be async. Unless +you are sure that no other component in your entire codebase is async, you should always +handle both string and promise cases. + +```tsx +// It may or may not have inner async components. +const html = ; + +if (html instanceof Promise) { + // I'm a promise, I should be awaited + console.log(await html); +} else { + // I'm a string, I can be used as is + console.log(html); +} +``` + +
+ +### Why there is no `context` API? + +We choose to not provide a `context` API. Here the reasons why: + +This library only outputs strings (or Promises that resolve to strings), we don't manage +lifecycle or state. So the main selling point to having a `context` API is to avoid +[prop drilling](https://www.geeksforgeeks.org/what-is-prop-drilling-and-how-to-avoid-it). + +A context Api would need to use a request identifier (internally called `rid`) to be safe +to access asynchronously. Take for example two requests that are being processed at the +same time, both of them use some async components. If we use a global context without +scoping it behind some sort of `rid`, the second request would override the context of the +first request. So for the first request, some part of the rendering (the non async part) +would use the correct context, but then the other section inside the async component would +use the context of the second request. **This is bad.**. Each component that uses context +would need to receive the `rid` as a prop. This will defeat the purpose of having a +context in the first place. The `rid` is needed to make the context async safe. + +The only way to maintain data consistency across concurrent renders without attaching a +request locator (`rid`), is by using +[ALS](https://nodejs.org/api/async_context.html#class-asynclocalstorage). However, this +approach introduces a lot of overhead and a significant performance penalty. + +Our recommendation is to use props. If you want to avoid prop drilling, you can use a +composition style of writing components. This is a common pattern in other JSX-based +libraries, and it works well with this library too. + +```tsx +// Drills only the required properties. +app.get('/', (request, response) => ( + + + + + + + +)); +``` + +
+ +## Alternative way to configure your tsconfig + +Is it possible to use this library using the old `jsx` and `jsxFactory` options in your +`tsconfig.json` file. This is not recommended, but it is possible. + +```json +{ + "compilerOptions": { + "jsx": "react", + "jsxFactory": "Html.createElement", + "jsxFragmentFactory": "Html.Fragment" + } +} +``` + +If you choose this approach keep in mind that you will need to manually import the `Html` +namespace in every file you use JSX. + +```tsx +import { Html } from '@kitajs/html'; + +const Html = ( +
+

Hello, world!

+

Welcome to the KitaJS HTML package.

+
+); +``` + +And also there is a light performance penalty when using this approach. It's minimal, but +it's there. + +## Migrating from HTML + +Migrating from plain HTML to JSX can be a pain to convert it all manually, as you will +find yourself hand placing quotes and closing void elements. + +You can use [**Html To Jsx**](https://magic.reactjs.net/htmltojsx.htm). + +```html + +
+ + +
+

Enter your HTML here

+``` + +Results into: + +```tsx +<> + {/* Hello world */} +
+ + +
+

Enter your HTML here

+ +``` + +
+ +### Htmx + +The usage of [htmx.org](https://htmx.org/) is super common with this project, this is why +we also provide type definitions for all HTMX attributes. + +You just need to add this triple slash directive to the top of your file: + +```tsx +/// + +const html = ( + // Type checking and intellisense for all HTMX attributes +
+ Click me! +
+); +``` + +Or you can use the type option in your tsconfig to import the types globally: + +```json +{ + "compilerOptions": { + "types": ["@kitajs/html/htmx.d.ts"] + } +} +``` + +
+ +### Alpinejs + +[Alpinejs](https://alpinejs.dev/) is commonly used with htmx. + +You just need to add this triple slash directive to the top of your file: + +```tsx +/// + +const html = ( + // Type checking and intellisense for all HTMX attributes +
...
+); +``` + +Or you can use the type option in your tsconfig to import the types globally: + +```json +{ + "compilerOptions": { + "types": ["@kitajs/html/alpine.d.ts"] + } +} +``` + +
+ +### Hotwire Turbo + +This project supports the usage of [Turbo Hotwire](https://turbo.hotwired.dev/). We +provide a separate export that you can use to provide type definitions for the elements +and attributes used with Turbo Hotwire. + +You just need to add this triple slash directive to the top of your file: + +```tsx +/// + +const html = ( + // Type checking and intellisense for all HTMX attributes + + Show all expanded messages in this frame. + +
Show response from this form within this frame.
+
+); +``` + +Or you can use the type option in your tsconfig to import the types globally: + +```json +{ + "compilerOptions": { + "types": ["@kitajs/html/hotwire-turbo.d.ts"] + } +} +``` + +
+ +### Base HTML templates + +Often you will have a "template" html with doctype, things on the head, body and so on... +Most users try to use them as a raw string and only use JSX for other components, but this +is a not a good idea as +[you will have problems with it](https://github.com/nicojs/typed-html/issues/46). + +But you can always concatenate strings, like in this required use-case for `` + +```tsx +export function Layout(props: Html.PropsWithChildren<{ head: string; title?: string }>) { + return ( + <> + {''} + + + + + {props.title || 'Hello World!'} + {props.head} + + {props.children} + + + ); +} + +const html = ( + + + ` +// which will lead to XSS vulnerabilities. +const html =
{content}
; + +// ✅ Content variable may have a value of ``, +// but it's safe to use because it will get escaped to = +// `<script>alert('xss')</script>`. +const html =
{content}
; + +// ⚠️ Content variable may have a value of ``, +// but variable starts with safe, so the error is suppressed. +const safeContent = content; +const html =
{safeContent}
; +``` + +
+ +### K602 + +Usage of safe attribute on a JSX element whose children contains other JSX elements. It +will lead to double escaping. If this is intended behavior, please extract the children +into a separate variable and use that instead. + +```tsx +// ❌ Safe attribute in the outer element will also escape inner elements. +// In this // case the tag will also be escaped, resulting into +// `<b>1</b>`. +const html = ( + + 1 + +); + +// ✅ Safe attribute in the inner element will escape only the inner element. +// In this case the tag will be escaped, resulting into +// `1`. +const html = ( + + 1 + +); +``` + +
+ +### K603 + +You are using a xss-prone element as a children of a component. Please wrap it into a +Html.escapeHtml() call or prepend it as a variable starting with `safe`. + +This error is similar to [K601](#k601), but instead of using `safe` native attribute, you +need to use `Html.escapeHtml()` function because its a component and not a native JSX. + +```tsx +// ❌ Content variable may have a value of `` +// which will lead to XSS vulnerabilities. +const html = {content}; + +// ✅ Content variable may have a value of ``, +// but it's safe to use because you manually call the escape function. +const html = {Html.escapeHtml(content)}; + +// ⚠️ Content variable may have a value of ``, +// but variable starts with safe, so the error is suppressed. +const safeContent = content; +const html = {safeContent}; +``` + +
+ +### K604 + +You are using the safe attribute on expressions that does not contain any XSS +vulnerabilities. Please remove the safe attribute or prepend your variable with `unsafe`. + +```tsx +// ⚠️ The variable will never have any harmful XSS content, so the safe attribute is +// not needed and can be removed. +const html =
{numberVariable}
; + +// ✅ This variable will never have any harmful XSS content, so we can use it +// as is. +const html =
{numberVariable}
; + +// ✅ You manually told this plugin that the variable is unsafe, so errors will +// be thrown. +const unsafeVariable = numberVariable; +const html =
{unsafeVariable}
; +``` + +
+ +## JSX + +For JSX support, please go to [kitajs/html](https://github.com/kitajs/html) for more +information. + +
+ +## Special cases + +1. Anything inside a `; + ``` + +2. Ternary and binary operations are evaluated in both sides separately and will throw + errors if any of the sides is not safe, even their condition never gets hit at runtime. + + ```tsx + const html =
{true ? safeContent : content}
; + // ~~~~~~~ + ``` + +
diff --git a/packages/ts-html-plugin/assets/preview.png b/packages/ts-html-plugin/assets/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..6640e47c573ed79a65564dd131d6d56a294a177d GIT binary patch literal 55866 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 Jm5J#4{cnAwaQOfL literal 0 HcmV?d00001 diff --git a/packages/ts-html-plugin/package.json b/packages/ts-html-plugin/package.json new file mode 100644 index 000000000..b908acf39 --- /dev/null +++ b/packages/ts-html-plugin/package.json @@ -0,0 +1,45 @@ +{ + "name": "@kitajs/ts-html-plugin", + "version": "1.3.4", + "homepage": "https://github.com/kitajs/html/tree/master/packages/ts-html-plugin#readme", + "bugs": "https://github.com/kitajs/html/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/kitajs/html.git", + "directory": "packages/ts-html-plugin" + }, + "funding": "https://github.com/kitajs/html?sponsor=1", + "license": "MIT", + "author": "Arthur Fiorette ", + "sideEffects": false, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "bin": { + "ts-html-plugin": "dist/cli.js", + "xss-scan": "dist/cli.js" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "dev": "tsc -p tsconfig.build.json --watch", + "prepack": "npm run build", + "test": "pnpm build && pnpm install && pnpm test-exec", + "test-exec": "node --require @swc-node/register --test test/**/*.test.ts" + }, + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.6.2", + "yargs": "^17.7.2" + }, + "devDependencies": { + "@swc-node/register": "^1.8.0", + "@swc/helpers": "^0.5.6", + "@types/node": "^20.11.24", + "@types/yargs": "^17.0.32", + "fast-defer": "^1.1.8", + "self": "file:." + }, + "peerDependencies": { + "@kitajs/html": "workspace:^", + "typescript": "^5.3.3" + } +} diff --git a/packages/ts-html-plugin/src/cli.ts b/packages/ts-html-plugin/src/cli.ts new file mode 100644 index 000000000..5040216c3 --- /dev/null +++ b/packages/ts-html-plugin/src/cli.ts @@ -0,0 +1,233 @@ +#!/usr/bin/env node + +import chalk from 'chalk'; +import fs from 'fs'; +import { EOL } from 'os'; +import path from 'path'; +import ts from 'typescript'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { recursiveDiagnoseJsxElements } from './util'; + +const { version } = require('../package.json'); + +const help = ` + +ts-html-plugin v${version} - A CLI tool & TypeScript LSP for finding XSS vulnerabilities in your TypeScript code. + +Usage: xss-scan [options] ... + ts-html-plugin [options] ... + +Options: + --cwd The current working directory to use (defaults to process.cwd()) + -p, --project The path to the tsconfig.json file to use (defaults to 'tsconfig.json') + -s, --simplified Use simplified diagnostics + -h, --help Show this help message + --version Show the version number + ... The files to check (defaults to all files in tsconfig.json) + +Examples: + $ xss-scan + $ xss-scan --cwd src + $ xss-scan --project tsconfig.build.json + $ xss-scan src/index.tsx src/App.tsx + +Exit codes: + 0 - No XSS vulnerabilities were found + 1 - XSS vulnerabilities were found + 2 - Only XSS warnings were found + +`.trim(); + +function readCompilerOptions(tsconfigPath: string) { + const { config, error } = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + + if (error) { + throw error; + } + + const { options, errors, fileNames } = ts.parseJsonConfigFileContent( + config, + ts.sys, + path.dirname(tsconfigPath), + undefined, + tsconfigPath + ); + + if (errors.length) { + return { errors }; + } + + return { options, fileNames, errors: undefined }; +} + +function prettyPrintErrorCount(diagnostics: ts.Diagnostic[], root: string) { + const files = new Map(); + + // Counts the amount of errors per file + for (const diagnostic of diagnostics) { + if (!diagnostic.file) { + continue; + } + + const file = files.get(diagnostic.file.fileName)!; + + if (file !== undefined) { + files.set(diagnostic.file.fileName, file + 1); + continue; + } + + files.set(diagnostic.file.fileName, 1); + } + + if (files.size > 1) { + console.error( + chalk.red(`Found a total of ${diagnostics.length} errors in ${files.size} files\n`) + ); + } + + for (const [file, amount] of files.entries()) { + console.error( + chalk.red( + `Found ${amount} error${amount === 1 ? '' : 's'} in ${path.relative(root, file)}` + ) + ); + } +} + +function fileExists(p: string) { + try { + fs.statSync(p); + return true; + } catch { + return false; + } +} + +async function main() { + const args = await yargs(hideBin(process.argv)).help(false).version(version).argv; + + if (args.help || args.h) { + console.log(help); + return process.exit(0); + } + + // Detects unknown arguments + for (const key in args) { + if (key === '_' || key === '$0') { + continue; + } + + switch (key) { + case 'cwd': + case 'project': + case 'p': + case 's': + case 'simplified': + continue; + default: + console.error(`Unknown argument: ${key}. Run --help for more information.`); + return process.exit(1); + } + } + + const root = args.cwd ? String(args.cwd) : process.cwd(); + + const tsconfigPath = String(args.project || args.p || 'tsconfig.json'); + + const simplified = !!(args.simplified || args.s); + + const diagnosticFormatter = simplified + ? ts.formatDiagnostics + : ts.formatDiagnosticsWithColorAndContext; + + if (!fileExists(tsconfigPath)) { + console.error((!simplified ? chalk.red : String)(`Could not find ${tsconfigPath}`)); + return process.exit(1); + } + + const tsconfig = readCompilerOptions(tsconfigPath); + + const diagnosticHost: ts.FormatDiagnosticsHost = { + getCurrentDirectory: () => root, + getCanonicalFileName: (fileName) => fileName, + getNewLine: () => EOL + }; + + if (tsconfig.errors) { + console.error(diagnosticFormatter(tsconfig.errors, diagnosticHost)); + return process.exit(1); + } + + let files = tsconfig.fileNames; + + if (args._.length) { + // Prefer the files passed as arguments, otherwise use the files in tsconfig.json + files = []; + + for (let i = 0; i < args._.length; i++) { + let file = String(args._[i]); + + if (!fileExists(file)) { + console.error( + (!simplified ? chalk.red : String)(`Could not find provided '${file}' file.`) + ); + return process.exit(1); + } + + if (!file.match(/(t|j)sx$/)) { + console.warn( + (!simplified ? chalk.yellow : String)( + `Provided '${file}' file is not a TSX/JSX file.` + ) + ); + continue; + } + + files.push(file); + } + } + + if (!files.length) { + console.error((!simplified ? chalk.red : String)(`No files were found to check.`)); + return process.exit(1); + } + + const program = ts.createProgram(files, tsconfig.options); + const typeChecker = program.getTypeChecker(); + const sources = program.getSourceFiles(); + + const diagnostics: ts.Diagnostic[] = []; + + for (const source of sources) { + const filename = source.fileName; + + // Not a tsx file, so don't do anything + if (!filename.match(/(t|j)sx$/)) { + continue; + } + + ts.forEachChild(source, function loopSourceNodes(node) { + recursiveDiagnoseJsxElements(ts as any, node, typeChecker, diagnostics); + }); + } + + if (diagnostics.length) { + const hasError = diagnostics.some( + (diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error + ); + + console.error(diagnosticFormatter(diagnostics, diagnosticHost)); + + if (!simplified) { + prettyPrintErrorCount(diagnostics, root); + } + + process.exit(hasError ? 1 : 2); + } + + console.log(chalk.green(`No XSS vulnerabilities found in ${files.length} files!`)); + process.exit(0); +} + +main().catch(console.error); diff --git a/packages/ts-html-plugin/src/errors.ts b/packages/ts-html-plugin/src/errors.ts new file mode 100644 index 000000000..a2e2db927 --- /dev/null +++ b/packages/ts-html-plugin/src/errors.ts @@ -0,0 +1,22 @@ +export const Xss = { + code: '0 K601' as any, + message: + 'Usage of xss-prone content without `safe` attribute. https://kitajs.github.io/ts-html-plugin#k601' +}; + +export const DoubleEscape = { + code: '0 K602' as any, + message: + 'Double escaping detected. Please remove the `safe` attribute. https://kitajs.github.io/ts-html-plugin#k602' +}; + +export const ComponentXss = { + code: '0 K603' as any, + message: + 'Xss-prone content inside a Component, wrap it into a Html.escapeHtml() call. https://kitajs.github.io/ts-html-plugin#k603' +}; + +export const UnusedSafe = { + code: '0 K604' as any, + message: 'Unused safe attribute. https://kitajs.github.io/ts-html-plugin#k604' +}; diff --git a/packages/ts-html-plugin/src/index.ts b/packages/ts-html-plugin/src/index.ts new file mode 100644 index 000000000..699467f92 --- /dev/null +++ b/packages/ts-html-plugin/src/index.ts @@ -0,0 +1,43 @@ +import type { default as TS, server } from 'typescript/lib/tsserverlibrary'; +import { proxyObject, recursiveDiagnoseJsxElements } from './util'; + +export = function initHtmlPlugin(modules: { typescript: typeof TS }) { + const ts = modules.typescript; + + return { + create(info: server.PluginCreateInfo) { + const proxy = proxyObject(info.languageService); + + proxy.getSemanticDiagnostics = function clonedSemanticDiagnostics(filename) { + const diagnostics = info.languageService.getSemanticDiagnostics(filename); + + // Not a tsx file, so don't do anything + if (!filename.endsWith('.tsx') && !filename.endsWith('.jsx')) { + return diagnostics; + } + + const program = info.languageService.getProgram(); + + if (!program) { + return diagnostics; + } + + const source = program?.getSourceFile(filename); + + if (!source) { + return diagnostics; + } + + const typeChecker = program.getTypeChecker(); + + ts.forEachChild(source, function loopSourceNodes(node) { + recursiveDiagnoseJsxElements(ts, node, typeChecker, diagnostics); + }); + + return diagnostics; + }; + + return proxy; + } + }; +}; diff --git a/packages/ts-html-plugin/src/util.ts b/packages/ts-html-plugin/src/util.ts new file mode 100644 index 000000000..893f23672 --- /dev/null +++ b/packages/ts-html-plugin/src/util.ts @@ -0,0 +1,356 @@ +import ts, { JsxFragment } from 'typescript'; +import type { + Diagnostic, + JsxElement, + JsxOpeningElement, + Node, + default as TS, + Type, + TypeChecker +} from 'typescript/lib/tsserverlibrary'; +import * as Errors from './errors'; + +const UPPERCASE = /[A-Z]/; +const ESCAPE_HTML_REGEX = /^(\w+\.)?(escapeHtml|e|escape)/i; + +/** If the node is a JSX element or fragment */ +export function isJsx(ts: typeof TS, node: TS.Node): node is JsxElement | JsxFragment { + return ts.isJsxElement(node) || ts.isJsxFragment(node); +} + +export function recursiveDiagnoseJsxElements( + ts: typeof TS, + node: Node, + typeChecker: TypeChecker, + original: Diagnostic[] +) { + ts.forEachChild(node, function loopSourceNodes(node) { + // Recurse through children first + ts.forEachChild(node, loopSourceNodes); + + // Adds children to the array + if (isJsx(ts, node)) { + // Diagnose the node + diagnoseJsxElement(ts, node, typeChecker, original); + } + }); + + // Filter out duplicates + for (let i = 0; i < original.length; i++) { + for (let j = i + 1; j < original.length; j++) { + if ( + original[i]!.start === original[j]!.start && + original[i]!.length === original[j]!.length + ) { + original.splice(j--, 1); + } + } + } +} + +function diagnostic( + node: ts.Node, + error: keyof typeof Errors, + category: keyof typeof TS.DiagnosticCategory +): ts.Diagnostic { + return { + category: ts.DiagnosticCategory[category], + messageText: Errors[error].message, + code: Errors[error].code, + file: node.getSourceFile(), + length: node.getWidth(), + start: node.getStart() + }; +} + +export function diagnoseJsxElement( + ts: typeof TS, + node: JsxElement | JsxFragment, + typeChecker: TypeChecker, + diagnostics: Diagnostic[] +): void { + const file = node.getSourceFile(); + + // Validations that does not applies to fragments + if (ts.isJsxElement(node)) { + // Script tags should be ignored + if (node.openingElement.tagName.getText() === 'script') { + return; + } + + const safeAttribute = getSafeAttribute(node.openingElement); + + // Safe mode warnings + if (safeAttribute) { + if ( + // Empty element + node.children.length === 0 || + // Only text elements + (node.children.length === 1 && node.children[0]!.kind === ts.SyntaxKind.JsxText) + ) { + diagnostics.push(diagnostic(safeAttribute, 'UnusedSafe', 'Warning')); + return; + } + + for (const exp of node.children) { + if ( + // JSX Element inside safe + ts.isJsxElement(exp) || + // Element is using safe with escapeHtml + (ts.isJsxExpression(exp) && exp.expression?.getText().match(ESCAPE_HTML_REGEX)) + ) { + diagnostics.push(diagnostic(safeAttribute, 'DoubleEscape', 'Error')); + continue; + } + + // Warn on unnecessary safe attributes + if ( + ts.isJsxExpression(exp) && + // has inner expression + exp.expression && + // is expression safe + isSafeAttribute( + ts, + typeChecker.getTypeAtLocation(exp.expression!), + exp.expression!, + typeChecker + ) && + // does not starts with unsafe + !exp.expression.getText().startsWith('unsafe') && + // Avoids double warnings + !diagnostics.some((d) => d.start === safeAttribute.pos + 1 && d.file === file) + ) { + diagnostics.push(diagnostic(safeAttribute, 'UnusedSafe', 'Warning')); + continue; + } + } + + return; + } + } + + // Look for expressions + for (const exp of node.children) { + if (!ts.isJsxExpression(exp)) { + continue; + } + + // Should always have an expression + if (!exp.expression) { + continue; + } + + diagnoseExpression( + ts, + exp.expression, + typeChecker, + diagnostics, + ts.isJsxElement(node) && !!node.openingElement.tagName.getText().match(UPPERCASE) + ); + } + + return; +} + +function diagnoseExpression( + ts: typeof TS, + node: ts.Expression, + typeChecker: TypeChecker, + diagnostics: Diagnostic[], + isComponent: boolean +): void { + // Unwrap parenthesis + if (ts.isParenthesizedExpression(node)) { + node = node.expression; + } + + // Ignores JSX elements as they are already diagnosed by the loopChildNodes + if (isJsx(ts, node)) { + return; + } + + // Checks both sides + if (ts.isBinaryExpression(node)) { + // Ignores operations which results in a boolean + switch (node.operatorToken.kind) { + case ts.SyntaxKind.EqualsEqualsEqualsToken: + case ts.SyntaxKind.EqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsToken: + case ts.SyntaxKind.GreaterThanToken: + case ts.SyntaxKind.GreaterThanEqualsToken: + case ts.SyntaxKind.LessThanEqualsToken: + case ts.SyntaxKind.LessThanToken: + case ts.SyntaxKind.InstanceOfKeyword: + case ts.SyntaxKind.InKeyword: + return; + } + + // We do not need to evaluate the left side of the expression + // as its value will only be used if its falsy, which cannot have + // XSS content + diagnoseExpression(ts, node.right, typeChecker, diagnostics, isComponent); + + return; + } + + // Checks the inner expression + if (ts.isConditionalExpression(node)) { + diagnoseExpression(ts, node.whenTrue, typeChecker, diagnostics, isComponent); + diagnoseExpression(ts, node.whenFalse, typeChecker, diagnostics, isComponent); + // ignore node.condition because its value will never be rendered + return; + } + + const type = typeChecker.getTypeAtLocation(node); + + // Safe can be ignored + if (isSafeAttribute(ts, type, node, typeChecker)) { + return; + } + + // Anything other than a identifier should be diagnosed + if (!ts.isIdentifier(node)) { + let hadJsx = false; + + for (const tag of node.getChildren()) { + if (!isJsx(ts, tag)) { + continue; + } + + hadJsx = true; + + diagnoseJsxElement( + ts, + tag as JsxElement | ts.JsxFragment, + typeChecker, + diagnostics + ); + } + + // If root JSX element found inside array, diagnose it, + // otherwise let the diagnostic pass + if (hadJsx) { + return; + } + } + + // Switch between component and element xss errors + if (isComponent || ts.isJsxFragment(node)) { + diagnostics.push(diagnostic(node, 'ComponentXss', 'Error')); + } else { + diagnostics.push(diagnostic(node, 'Xss', 'Error')); + } +} + +export function isSafeAttribute( + ts: typeof TS, + type: Type | undefined, + expression: ts.Expression, + checker: TypeChecker +): boolean { + // Nothing to do if type cannot be resolved + if (!type) { + return true; + } + + // Any type is never safe + if (type.flags & ts.TypeFlags.Any) { + return false; + } + + if (type.aliasSymbol) { + // Allows JSX.Element + if ( + type.aliasSymbol.escapedName === 'Element' && + // @ts-expect-error - Fast way of checking + type.aliasSymbol.parent?.escapedName === 'JSX' && + // Only allows in .map(), other method calls or the expression itself + (ts.isCallExpression(expression) || ts.isIdentifier(expression)) + ) { + return true; + } + + // Allows Html.Children + if ( + type.aliasSymbol.escapedName === 'Children' && + // @ts-expect-error - When using export namespace X {} and export default X, parent.escapedName + // ends up as the original namespace name, not the quoted export name. + (type.aliasSymbol.parent?.escapedName === 'Html' || + // @ts-expect-error - When using export as namespace X, parent.escapedName ends up + // as a complete (without resolving symlinks) quoted import path to its original file. + type.aliasSymbol.parent?.escapedName.endsWith('@kitajs/html/index"') || + // This is needed because of the resolved path of the parent if is installed with pnpm is a symlink + // that ts resolves to the original file path, so the path is not related to the node_modules but instead + // is absolute to the file system (this is only here because of the monorepo setup, it is not needed when used as a package) + (process.env.KITA_TS_HTML_PLUGIN_TESTING === 'true' && + // @ts-expect-error - When using export as namespace X, parent.escapedName ends up + type.aliasSymbol.parent?.escapedName.endsWith('packages/html/index"'))) + ) { + return true; + } + } + + // Union types should be checked recursively + if (type.isUnion()) { + return (type as TS.UnionType).types.every((t) => + isSafeAttribute(ts, t, expression, checker) + ); + } + + // For Array or Promise, we check the type of the first generic + if (checker.isArrayType(type) || type.symbol?.escapedName === 'Promise') { + return isSafeAttribute( + ts, + (type as any).resolvedTypeArguments?.[0], + expression, + checker + ); + } + + // We allow literal string types here, as if they have XSS content, + // the user has explicitly written it + if ( + // Non string types cannot have XSS values + !(type.flags & ts.TypeFlags.String) && + // Objects may have toString() overridden + !(type.flags & ts.TypeFlags.Object) + ) { + return true; + } + + const text = expression.getText(); + + if ( + // Variables starting with safe are suppressed + text.startsWith('safe') || + // Starts with a call to a escapeHtml function name + text.match(ESCAPE_HTML_REGEX) + ) { + return true; + } + + return false; +} + +export function getSafeAttribute(element: JsxOpeningElement) { + for (const attribute of element.attributes.properties) { + if (attribute.getText() === 'safe') { + return attribute; + } + } + + return undefined; +} + +export function proxyObject(obj: T): T { + const proxy: T = Object.create(null); + + for (let k of Object.keys(obj) as Array) { + const x = obj[k]!; + // @ts-expect-error - JS runtime trickery which is tricky to type tersely + proxy[k] = (...args: Array<{}>) => x.apply(obj, args); + } + + return proxy; +} diff --git a/packages/ts-html-plugin/test/arrays.test.ts b/packages/ts-html-plugin/test/arrays.test.ts new file mode 100644 index 000000000..0e7273c31 --- /dev/null +++ b/packages/ts-html-plugin/test/arrays.test.ts @@ -0,0 +1,19 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { TSLangServer } from './util/lang-server'; + +it('Lists and arrays can be used normally', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + const list: JSX.Element[] = []; + + export default ( + <> +
{list}
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, []); +}); diff --git a/packages/ts-html-plugin/test/children.test.ts b/packages/ts-html-plugin/test/children.test.ts new file mode 100644 index 000000000..6fa4c3e73 --- /dev/null +++ b/packages/ts-html-plugin/test/children.test.ts @@ -0,0 +1,35 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { TSLangServer } from './util/lang-server'; + +it('Ensure PropsWithChildren works as normal', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export interface Extension extends PropsWithChildren { + user?: { name: string }; + } + + export function Test({ children }: Extension) { + return
{children}
; + } + + const element =
; + + export default ( + <> +
+ + {element} + +
+ +
+ {element} +
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, []); +}); diff --git a/packages/ts-html-plugin/test/component-xss.test.ts b/packages/ts-html-plugin/test/component-xss.test.ts new file mode 100644 index 000000000..ccac04d47 --- /dev/null +++ b/packages/ts-html-plugin/test/component-xss.test.ts @@ -0,0 +1,119 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { ComponentXss } from '../src/errors'; +import { TSLangServer } from './util/lang-server'; + +it('Ensure children are safe', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> + {/* error */} +
+ {['a', 'b', 'c'].map((i) => ( + {i} + ))} +
+ + {['a', 'b', 'c'].map((i) => (i === 'a' ? safeString : {i}))} + + + {/* safe */} +
+ {[1, 2, 3].map((i) => ( + {i} + ))} +
+
+ {['a', 'b', 'c'].map((i) => ( + {Html.escapeHtml(i)} + ))} +
+
+ {Html.escapeHtml(object)} + asd +
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + start: { line: 39, offset: 23 }, + end: { line: 39, offset: 24 }, + text: ComponentXss.message, + code: ComponentXss.code, + category: 'error' + }, + { + start: { line: 43, offset: 77 }, + end: { line: 43, offset: 78 }, + text: ComponentXss.message, + code: ComponentXss.code, + category: 'error' + } + ]); +}); + +it('Ensure children are safe using "e" tag function', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> + {/* error */} +
+ {['a', 'b', 'c'].map((i) => ( + {i} + ))} +
+ + {['a', 'b', 'c'].map((i) => (i === 'a' ? safeString : {i}))} + + + {/* safe */} +
+ {[1, 2, 3].map((i) => ( + {i} + ))} +
+
+ {['a', 'b', 'c'].map((i) => ( + ${'{Html.e`${i}`}'} + ))} +
+
+ ${'{Html.e`${object}`}'} + asd +
+
+ {['a', 'b', 'c'].map((i) => ( + ${'{e`${i}`}'} + ))} +
+
+ ${'{e`${object}`}'} + asd +
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + start: { line: 39, offset: 23 }, + end: { line: 39, offset: 24 }, + text: ComponentXss.message, + code: ComponentXss.code, + category: 'error' + }, + { + start: { line: 43, offset: 77 }, + end: { line: 43, offset: 78 }, + text: ComponentXss.message, + code: ComponentXss.code, + category: 'error' + } + ]); +}); diff --git a/packages/ts-html-plugin/test/double-escape.test.ts b/packages/ts-html-plugin/test/double-escape.test.ts new file mode 100644 index 000000000..3c72506b9 --- /dev/null +++ b/packages/ts-html-plugin/test/double-escape.test.ts @@ -0,0 +1,63 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { DoubleEscape } from '../src/errors'; +import { TSLangServer } from './util/lang-server'; + +it('Avoid escaping twice', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> +
+
{number}
+
+
+ {Html.escapeHtml(object)} + asd +
+
{Html.escapeHtml(object)}
+ ${'
{Html.e`${object}`}
'} + ${'
{e`${object}`}
'} + + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + start: { line: 36, offset: 14 }, + end: { line: 36, offset: 18 }, + text: DoubleEscape.message, + code: DoubleEscape.code, + category: 'error' + }, + { + start: { line: 39, offset: 14 }, + end: { line: 39, offset: 18 }, + text: DoubleEscape.message, + code: DoubleEscape.code, + category: 'error' + }, + { + start: { line: 43, offset: 14 }, + end: { line: 43, offset: 18 }, + text: DoubleEscape.message, + code: DoubleEscape.code, + category: 'error' + }, + { + start: { line: 44, offset: 14 }, + end: { line: 44, offset: 18 }, + text: DoubleEscape.message, + code: DoubleEscape.code, + category: 'error' + }, + { + start: { line: 45, offset: 14 }, + end: { line: 45, offset: 18 }, + text: DoubleEscape.message, + code: DoubleEscape.code, + category: 'error' + } + ]); +}); diff --git a/packages/ts-html-plugin/test/operators.test.ts b/packages/ts-html-plugin/test/operators.test.ts new file mode 100644 index 000000000..7f5255b12 --- /dev/null +++ b/packages/ts-html-plugin/test/operators.test.ts @@ -0,0 +1,126 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { Xss } from '../src/errors'; +import { TSLangServer } from './util/lang-server'; + +it('Operators are evaluated normally', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> +
{boolean ? number : html}
+
{boolean ? number : (html)}
+
{boolean ? number :
{html}
}
+
{boolean ? (number) : (
{html}
)}
+ +
{number && html}
+
{number && (html)}
+
{number && (
{html}
)}
+
{number &&
{html}
}
+ +
{html || safeString}
+
{(html) || safeString}
+
{
{html}
|| safeString}
+
{(
{html}
) || safeString}
+ + {/* safe */} +
{boolean ? number :
Safe!
}
+
{boolean ? number : (
Safe!
)}
+
{boolean ? (number) : (
Deep safe!
)}
+ +
{number &&
Safe!
}
+
{number && (
Safe!
)}
+
{number && (
Deep safe!
)}
+ +
{
Safe!
|| safeString}
+
{(
Safe!
) || safeString}
+
{(
Deep safe!
) || safeString}
+ + {/* Booleans */} +
{html !== 'Html'}
+
{html >= 'Html'}
+
{html <= 'Html'}
+
{html > 'Html'}
+
{html < 'Html'}
+
{'html' in ({ html })}
+
{new String(html) instanceof String}
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + start: { line: 36, offset: 34 }, + end: { line: 36, offset: 38 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 37, offset: 35 }, + end: { line: 37, offset: 39 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 38, offset: 40 }, + end: { line: 38, offset: 44 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 39, offset: 48 }, + end: { line: 39, offset: 52 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + + { + start: { line: 41, offset: 25 }, + end: { line: 41, offset: 29 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 42, offset: 26 }, + end: { line: 42, offset: 30 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 43, offset: 37 }, + end: { line: 43, offset: 41 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 44, offset: 31 }, + end: { line: 44, offset: 35 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + + { + start: { line: 48, offset: 21 }, + end: { line: 48, offset: 25 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 49, offset: 27 }, + end: { line: 49, offset: 31 }, + text: Xss.message, + code: Xss.code, + category: 'error' + } + ]); +}); diff --git a/packages/ts-html-plugin/test/readme.test.ts b/packages/ts-html-plugin/test/readme.test.ts new file mode 100644 index 000000000..a998612ca --- /dev/null +++ b/packages/ts-html-plugin/test/readme.test.ts @@ -0,0 +1,26 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { Xss } from '../src/errors'; +import { TSLangServer } from './util/lang-server'; + +it('Ensures readme checks will throw error', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> +
{String.name}
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + category: 'error', + code: Xss.code, + end: { line: 36, offset: 26 }, + start: { line: 36, offset: 15 }, + text: Xss.message + } + ]); +}); diff --git a/packages/ts-html-plugin/test/safe.test.ts b/packages/ts-html-plugin/test/safe.test.ts new file mode 100644 index 000000000..3051c0c49 --- /dev/null +++ b/packages/ts-html-plugin/test/safe.test.ts @@ -0,0 +1,69 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { TSLangServer } from './util/lang-server'; + +it('Allow correct xss usage', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> +
+
{date.getTime()}
+
{date.toISOString()}
+
{safeString}
+
{promiseHtml}
+
{promiseNumber}
+
{html}
+
{number}
+
hello {html}
+
{unsafeNumber}
+
{union}
+ +
+ {['asda', 'b', 'c'].map((i) => ( + <>{Html.escapeHtml(i)} + ))} +
+
+ {['asda', 'b', 'c'].map((i) => ( + ${'<>{Html.e`${i} some text`}'} + ))} +
+
+ {['asda', 'b', 'c'].map((i) => ( + ${'<>{Html.escape`${i} some text`}'} + ))} +
+
+ {['a', 'b', 'c'].map((i) => ( +
{i}
+ ))} +
+
+ {[1, 2, 3].map((i) => ( + <>{i} + ))} +
+
{[1, 2, 3].map((i) => i)}
+
{['a', 'b', 'c'].map((i) => i)}
+
+ {'literal'} + {1} +
+
{Html.escapeHtml(html)}
+
{Html.escapeHtml(object)}
+ ${'
{Html.e`${html}`}
'} + ${'
{Html.e`${object}`}
'} + ${'
{e`${html}`}
'} + ${'
{e`${object}`}
'} + ${'
{Html.escape`${html}`}
'} + ${'
{Html.escape`${object}`}
'} +
{boolean ? number : safeString}
+
{number || safeString}
+ + ); +`; + + assert.strictEqual(diagnostics.body.length, 0); +}); diff --git a/packages/ts-html-plugin/test/tsconfig.json b/packages/ts-html-plugin/test/tsconfig.json new file mode 100644 index 000000000..0775d5429 --- /dev/null +++ b/packages/ts-html-plugin/test/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "plugins": [{ "name": "self" }] + }, + "include": ["**/*.tsx", "*.test.ts"] +} diff --git a/packages/ts-html-plugin/test/unsafe-tags.test.ts b/packages/ts-html-plugin/test/unsafe-tags.test.ts new file mode 100644 index 000000000..f6ddabfcd --- /dev/null +++ b/packages/ts-html-plugin/test/unsafe-tags.test.ts @@ -0,0 +1,74 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { Xss } from '../src/errors'; +import { TSLangServer } from './util/lang-server'; + +it('Detect xss prone usage', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> +
+
{html}
+
{union}
+
+ {['a', 'b', 'c'].map((i) => ( + <>{i} + ))} + {['a', 'b', 'c'].map((i) => ( +
{i}
+ ))} +
+
{['a', 'b', 'c'].map((i) => i)}
+
{['a', 'b', 'c'].map((safeI) => safeI)}
+
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + start: { line: 37, offset: 15 }, + end: { line: 37, offset: 19 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 38, offset: 15 }, + end: { line: 38, offset: 20 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 41, offset: 16 }, + end: { line: 41, offset: 17 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 44, offset: 19 }, + end: { line: 44, offset: 20 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 47, offset: 15 }, + end: { line: 47, offset: 44 }, + text: Xss.message, + code: Xss.code, + category: 'error' + }, + { + start: { line: 48, offset: 15 }, + end: { line: 48, offset: 52 }, + text: Xss.message, + code: Xss.code, + category: 'error' + } + ]); +}); diff --git a/packages/ts-html-plugin/test/util/index.tsx b/packages/ts-html-plugin/test/util/index.tsx new file mode 100644 index 000000000..bfff3e73c --- /dev/null +++ b/packages/ts-html-plugin/test/util/index.tsx @@ -0,0 +1,2 @@ +// The typescript LSP checks if the index.tsx file exists, +// and not its contents as its already provided in each test. diff --git a/packages/ts-html-plugin/test/util/lang-server.ts b/packages/ts-html-plugin/test/util/lang-server.ts new file mode 100644 index 000000000..52015f00b --- /dev/null +++ b/packages/ts-html-plugin/test/util/lang-server.ts @@ -0,0 +1,206 @@ +import { ChildProcess, fork } from 'child_process'; +import { EventEmitter } from 'events'; +import { Deferred, deferred } from 'fast-defer'; +import { statSync } from 'fs'; +import path from 'path'; +import ts from 'typescript/lib/tsserverlibrary'; + +/** All requests used in tests */ +export type Requests = + | ts.server.protocol.OpenRequest + | ts.server.protocol.SemanticDiagnosticsSyncRequest; + +try { + statSync(require.resolve('self')); +} catch (error) { + throw new Error('You must run pnpm build before running tests'); +} + +const TEST_FILE = path.join(__dirname, 'index.tsx'); +const ROOT = path.join(__dirname, '..'); + +export const TEST_HELPERS = /* tsx */ ` + import Html, { type PropsWithChildren, e } from '@kitajs/html'; + + const date = new Date(); + const safeString: string = 'safe'; + const promiseHtml = Promise.resolve(
Hello
); + const promiseNumber = Promise.resolve(1227); + const html: string = '
Hello
'; + const number = 1227; + const unsafeNumber = 1227; + const union = 1 as string | number; + const boolean = true as boolean; + const object = {}; + + function Component(props: PropsWithChildren) { + return
{props.children}
; + } + + // just to avoid unused variable error + if (process.env.NEVER) { + console.log({ + date, e, Html, + safeString, + promiseHtml, + promiseNumber, + html, + number, + unsafeNumber, + union, + boolean, + object, + Component + }); + } +`.trim(); + +export class TSLangServer { + responseEventEmitter = new EventEmitter(); + responseCommandEmitter = new EventEmitter(); + errorEmitter = new EventEmitter(); + + exitPromise: Deferred; + isClosed = false; + server: ChildProcess; + sequence = 0; + + constructor( + projectPath: string, + private readonly debug = false + ) { + this.server = fork(require.resolve('typescript/lib/tsserver'), { + cwd: projectPath, + stdio: ['pipe', 'pipe', 'pipe', 'ipc'], + env: { + TSS_LOG: debug ? `-level verbose -file ${projectPath}/tss.log` : undefined, + KITA_TS_HTML_PLUGIN_TESTING: 'true' + } + }); + + this.exitPromise = deferred(); + this.server.on('exit', this.exitPromise.resolve); + this.server.on('error', this.exitPromise.reject); + + this.server.stdout!.setEncoding('utf-8'); + + this.server.stdout!.on('data', (data) => { + const obj = JSON.parse(data.split('\n', 3)[2]); + + if (this.debug) { + console.dir(obj, { depth: 10 }); + } + + if (obj.success === false) { + this.errorEmitter.emit(obj.type === 'event' ? obj.event : obj.command, obj); + + // Error is fatal, close the server + if (!this.isClosed) { + this.isClosed = true; + this.server.stdin!.end(); + } + } else if (obj.type === 'event') { + this.responseEventEmitter.emit(obj.event, obj); + } else if (obj.type === 'response') { + this.responseCommandEmitter.emit(obj.command, obj); + } + }); + } + + /** Opens the project, sends diagnostics request and returns the response */ + async openWithDiagnostics(content: TemplateStringsArray, ...args: any[]) { + const fileContent = TEST_HELPERS + '\n' + String.raw(content, ...args).trim(); + + if (this.debug) { + console.log(fileContent); + } + + await this.send({ + command: ts.server.protocol.CommandTypes.Open, + arguments: { + file: TEST_FILE, + fileContent, + scriptKindName: 'TSX', + projectRootPath: ROOT + } + }); + + // One of these events will be emitted after opening the project + await Promise.race([ + this.waitResponse('open'), + this.waitEvent('projectLoadingFinish') + ]); + + await this.send({ + command: ts.server.protocol.CommandTypes.SemanticDiagnosticsSync, + arguments: { + file: TEST_FILE, + fileContent, + scriptKindName: 'TSX', + projectRootPath: ROOT + } + }); + + return this.waitResponse('semanticDiagnosticsSync'); + } + + send(command: Omit) { + const response = deferred(); + + this.server.stdin!.write(this.formatCommand(command), (err) => + err ? response.reject(err) : response.resolve() + ); + + return response; + } + + private formatCommand(command: Omit) { + return ( + JSON.stringify(Object.assign({ seq: ++this.sequence, type: 'request' }, command)) + + '\n' + ); + } + + waitEvent(eventName: string) { + return new Promise((resolve, reject) => { + const success = (data: any) => { + this.errorEmitter.removeListener(eventName, success); + resolve(data); + }; + + const error = (err: any) => { + this.responseEventEmitter.removeListener(eventName, success); + reject(err); + }; + + this.responseEventEmitter.once(eventName, success); + this.errorEmitter.once(eventName, error); + }); + } + + waitResponse(commandName: `${ts.server.protocol.CommandTypes}`) { + return new Promise((resolve, reject) => { + const success = (data: any) => { + this.errorEmitter.removeListener(commandName, success); + resolve(data); + }; + + const error = (err: any) => { + this.responseCommandEmitter.removeListener(commandName, success); + reject(err); + }; + + this.responseCommandEmitter.once(commandName, success); + this.errorEmitter.once(commandName, error); + }); + } + + [Symbol.asyncDispose]() { + if (!this.isClosed) { + this.isClosed = true; + this.server.stdin!.end(); + } + + return this.exitPromise; + } +} diff --git a/packages/ts-html-plugin/test/warn.test.ts b/packages/ts-html-plugin/test/warn.test.ts new file mode 100644 index 000000000..02cf68657 --- /dev/null +++ b/packages/ts-html-plugin/test/warn.test.ts @@ -0,0 +1,60 @@ +import assert from 'node:assert'; +import { it } from 'node:test'; +import { UnusedSafe } from '../src/errors'; +import { TSLangServer } from './util/lang-server'; + +it('Warn on unused `safe` tags', async () => { + await using server = new TSLangServer(__dirname); + + const diagnostics = await server.openWithDiagnostics/* tsx */ ` + export default ( + <> +
+ {number} {safeString} +
+
{safeString}
+
{promiseNumber}
+
+
+ + ); +`; + + assert.deepStrictEqual(diagnostics.body, [ + { + start: { line: 36, offset: 14 }, + end: { line: 36, offset: 18 }, + text: UnusedSafe.message, + code: UnusedSafe.code, + category: 'warning' + }, + { + start: { line: 39, offset: 14 }, + end: { line: 39, offset: 18 }, + text: UnusedSafe.message, + code: UnusedSafe.code, + category: 'warning' + }, + { + start: { line: 40, offset: 14 }, + end: { line: 40, offset: 18 }, + text: UnusedSafe.message, + code: UnusedSafe.code, + category: 'warning' + }, + { + start: { line: 41, offset: 14 }, + end: { line: 41, offset: 18 }, + text: UnusedSafe.message, + code: UnusedSafe.code, + category: 'warning' + }, + { + start: { line: 42, offset: 14 }, + end: { line: 42, offset: 18 }, + text: UnusedSafe.message, + code: UnusedSafe.code, + category: 'warning' + } + ]); +}); diff --git a/packages/ts-html-plugin/tsconfig.build.json b/packages/ts-html-plugin/tsconfig.build.json new file mode 100644 index 000000000..b90fc83e0 --- /dev/null +++ b/packages/ts-html-plugin/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src"] +} diff --git a/packages/ts-html-plugin/tsconfig.json b/packages/ts-html-plugin/tsconfig.json new file mode 100644 index 000000000..6e521c8a2 --- /dev/null +++ b/packages/ts-html-plugin/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2022", + "jsx": "react-jsx", + "jsxImportSource": "@kitajs/html", + "module": "CommonJS", + "moduleResolution": "Node", + "outDir": "dist", + "plugins": [{ "name": "self" }] + }, + "include": ["src", "test"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a0dac817..5b49234b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,111 +4,506 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - '@kitajs/ts-html-plugin': - specifier: '>=1.3.3' - version: 1.3.3(@kitajs/html@3.0.11)(typescript@5.4.2) - csstype: - specifier: ^3.1.3 - version: 3.1.3 - -devDependencies: - '@arthurfiorette/prettier-config': - specifier: ^1.0.12 - version: 1.0.12(prettier@3.2.5)(typescript@5.4.2) - '@types/jsdom': - specifier: ^21.1.6 - version: 21.1.6 - '@types/node': - specifier: ^20.11.5 - version: 20.11.25 - benny: - specifier: ^3.7.1 - version: 3.7.1 - c8: - specifier: ^9.1.0 - version: 9.1.0 - common-tags: - specifier: ^1.8.2 - version: 1.8.2 - ghtml: - specifier: ^1.0.1 - version: 1.0.1 - jsdom: - specifier: ^24.0.0 - version: 24.0.0 - mitata: - specifier: ^0.1.6 - version: 0.1.11 - prettier: - specifier: ^3.2.4 - version: 3.2.5 - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - tslib: - specifier: ^2.6.2 - version: 2.6.2 - typed-html: - specifier: ^3.0.1 - version: 3.0.1 - typescript: - specifier: ^5.3.3 - version: 5.4.2 +importers: + + .: + devDependencies: + '@arthurfiorette/prettier-config': + specifier: ^1.0.12 + version: 1.0.12(prettier@3.2.5)(typescript@5.3.3) + '@changesets/changelog-github': + specifier: ^0.5.0 + version: 0.5.0 + '@changesets/cli': + specifier: ^2.27.1 + version: 2.27.1 + '@kitajs/html': + specifier: workspace:* + version: link:packages/html + husky: + specifier: ^9.0.11 + version: 9.0.11 + prettier: + specifier: ^3.2.5 + version: 3.2.5 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + + benchmarks/kitajs: + dependencies: + '@kitajs/html': + specifier: workspace:* + version: link:../../packages/html + tslib: + specifier: ^2.6.2 + version: 2.6.2 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + + benchmarks/reactjsx: + dependencies: + react: + specifier: ^18.2.0 + version: 18.2.0 + tslib: + specifier: ^2.6.2 + version: 2.6.2 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + devDependencies: + '@types/node': + specifier: ^20.11.20 + version: 20.11.20 + '@types/react': + specifier: ^18.2.60 + version: 18.2.61 + + benchmarks/runner: + dependencies: + '@kitajs/bench-html-kitajs': + specifier: workspace:* + version: link:../kitajs + '@kitajs/bench-html-reactjsx': + specifier: workspace:* + version: link:../reactjsx + '@kitajs/bench-html-templates': + specifier: workspace:* + version: link:../templates + '@kitajs/bench-html-typed-html': + specifier: workspace:* + version: link:../typed-html + common-tags: + specifier: ^1.8.2 + version: 1.8.2 + ghtml: + specifier: ^1.0.1 + version: 1.0.1 + mitata: + specifier: ^0.1.11 + version: 0.1.11 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + + benchmarks/templates: + dependencies: + tslib: + specifier: ^2.6.2 + version: 2.6.2 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + devDependencies: + '@types/node': + specifier: ^20.11.20 + version: 20.11.20 + + benchmarks/typed-html: + dependencies: + tslib: + specifier: ^2.6.2 + version: 2.6.2 + typed-html: + specifier: ^3.0.1 + version: 3.0.1 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + + packages/fastify-html-plugin: + dependencies: + '@kitajs/html': + specifier: workspace:^ + version: link:../html + '@kitajs/ts-html-plugin': + specifier: workspace:^ + version: link:../ts-html-plugin + fastify-plugin: + specifier: ^4.0.0 + version: 4.5.1 + devDependencies: + '@fastify/formbody': + specifier: ^7.4.0 + version: 7.4.0 + '@swc-node/register': + specifier: ^1.8.0 + version: 1.8.0(@swc/core@1.4.2)(@swc/types@0.1.5)(typescript@5.3.3) + '@swc/helpers': + specifier: ^0.5.6 + version: 0.5.6 + '@types/jsdom': + specifier: ^21.1.6 + version: 21.1.6 + '@types/node': + specifier: ^20.11.24 + version: 20.11.24 + c8: + specifier: ^9.1.0 + version: 9.1.0 + fastify: + specifier: ^4.26.1 + version: 4.26.1 + jsdom: + specifier: ^24.0.0 + version: 24.0.0 + tsd: + specifier: ^0.30.7 + version: 0.30.7 + tslib: + specifier: ^2.6.2 + version: 2.6.2 + + packages/html: + dependencies: + '@kitajs/ts-html-plugin': + specifier: workspace:^ + version: link:../ts-html-plugin + csstype: + specifier: ^3.1.3 + version: 3.1.3 + devDependencies: + '@types/jsdom': + specifier: ^21.1.6 + version: 21.1.6 + '@types/node': + specifier: ^20.11.24 + version: 20.11.24 + c8: + specifier: ^9.1.0 + version: 9.1.0 + jsdom: + specifier: ^24.0.0 + version: 24.0.0 + tslib: + specifier: ^2.6.2 + version: 2.6.2 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + + packages/ts-html-plugin: + dependencies: + '@kitajs/html': + specifier: workspace:^ + version: link:../html + chalk: + specifier: ^4.1.2 + version: 4.1.2 + tslib: + specifier: ^2.6.2 + version: 2.6.2 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + yargs: + specifier: ^17.7.2 + version: 17.7.2 + devDependencies: + '@swc-node/register': + specifier: ^1.8.0 + version: 1.8.0(@swc/core@1.4.2)(@swc/types@0.1.5)(typescript@5.3.3) + '@swc/helpers': + specifier: ^0.5.6 + version: 0.5.6 + '@types/node': + specifier: ^20.11.24 + version: 20.11.24 + '@types/yargs': + specifier: ^17.0.32 + version: 17.0.32 + fast-defer: + specifier: ^1.1.8 + version: 1.1.8 + self: + specifier: file:. + version: file:packages/ts-html-plugin(@kitajs/html@packages+html)(typescript@5.3.3) packages: - /@arrows/array@1.4.1: - resolution: {integrity: sha512-MGYS8xi3c4tTy1ivhrVntFvufoNzje0PchjEz6G/SsWRgUKxL4tKwS6iPdO8vsaJYldagAeWMd5KRD0aX3Q39g==} + /@arthurfiorette/prettier-config@1.0.12(prettier@3.2.5)(typescript@5.3.3): + resolution: {integrity: sha512-jR36bHWLVv71FuKkU9XYeBe6ry/AN98EDh0WVyfo3QwjJFbhpeElFTS4nluwxeXfu6nJ8/Hv8bJf6cYeXzfeoA==} + hasBin: true + peerDependencies: + prettier: ^3.0.3 + dependencies: + prettier: 3.2.5 + prettier-plugin-jsdoc: 1.1.1(prettier@3.2.5) + prettier-plugin-organize-imports: 3.2.3(prettier@3.2.5)(typescript@5.3.3) + prettier-plugin-packagejson: 2.4.6(prettier@3.2.5) + transitivePeerDependencies: + - '@volar/vue-language-plugin-pug' + - '@volar/vue-typescript' + - supports-color + - typescript + dev: true + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/runtime@7.24.0: + resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==} + engines: {node: '>=6.9.0'} dependencies: - '@arrows/composition': 1.2.2 + regenerator-runtime: 0.14.1 dev: true - /@arrows/composition@1.2.2: - resolution: {integrity: sha512-9fh1yHwrx32lundiB3SlZ/VwuStPB4QakPsSLrGJFH6rCXvdrd060ivAZ7/2vlqPnEjBkPRRXOcG1YOu19p2GQ==} + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@changesets/apply-release-plan@7.0.0: + resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==} + dependencies: + '@babel/runtime': 7.24.0 + '@changesets/config': 3.0.0 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.5.4 dev: true - /@arrows/dispatch@1.0.3: - resolution: {integrity: sha512-v/HwvrFonitYZM2PmBlAlCqVqxrkIIoiEuy5bQgn0BdfvlL0ooSBzcPzTMrtzY8eYktPyYcHg8fLbSgyybXEqw==} + /@changesets/assemble-release-plan@6.0.0: + resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==} dependencies: - '@arrows/composition': 1.2.2 + '@babel/runtime': 7.24.0 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.0.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.5.4 dev: true - /@arrows/error@1.0.2: - resolution: {integrity: sha512-yvkiv1ay4Z3+Z6oQsUkedsQm5aFdyPpkBUQs8vejazU/RmANABx6bMMcBPPHI4aW43VPQmXFfBzr/4FExwWTEA==} + /@changesets/changelog-git@0.2.0: + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + dependencies: + '@changesets/types': 6.0.0 dev: true - /@arrows/multimethod@1.4.1: - resolution: {integrity: sha512-AZnAay0dgPnCJxn3We5uKiB88VL+1ZIF2SjZohLj6vqY2UyvB/sKdDnFP+LZNVsTC5lcnGPmLlRRkAh4sXkXsQ==} + /@changesets/changelog-github@0.5.0: + resolution: {integrity: sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==} dependencies: - '@arrows/array': 1.4.1 - '@arrows/composition': 1.2.2 - '@arrows/error': 1.0.2 - fast-deep-equal: 3.1.3 + '@changesets/get-github-info': 0.6.0 + '@changesets/types': 6.0.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding dev: true - /@arthurfiorette/prettier-config@1.0.12(prettier@3.2.5)(typescript@5.4.2): - resolution: {integrity: sha512-jR36bHWLVv71FuKkU9XYeBe6ry/AN98EDh0WVyfo3QwjJFbhpeElFTS4nluwxeXfu6nJ8/Hv8bJf6cYeXzfeoA==} + /@changesets/cli@2.27.1: + resolution: {integrity: sha512-iJ91xlvRnnrJnELTp4eJJEOPjgpF3NOh4qeQehM6Ugiz9gJPRZ2t+TsXun6E3AMN4hScZKjqVXl0TX+C7AB3ZQ==} hasBin: true - peerDependencies: - prettier: ^3.0.3 dependencies: - prettier: 3.2.5 - prettier-plugin-jsdoc: 1.1.1(prettier@3.2.5) - prettier-plugin-organize-imports: 3.2.3(prettier@3.2.5)(typescript@5.4.2) - prettier-plugin-packagejson: 2.4.6(prettier@3.2.5) + '@babel/runtime': 7.24.0 + '@changesets/apply-release-plan': 7.0.0 + '@changesets/assemble-release-plan': 6.0.0 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.0 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.0.0 + '@changesets/get-release-plan': 4.0.0 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.0 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + chalk: 2.4.2 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + meow: 6.1.1 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.1.3 + resolve-from: 5.0.0 + semver: 7.5.4 + spawndamnit: 2.0.0 + term-size: 2.2.1 + tty-table: 4.2.3 + dev: true + + /@changesets/config@3.0.0: + resolution: {integrity: sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA==} + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.0.0 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.5 + dev: true + + /@changesets/errors@0.2.0: + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + dependencies: + extendable-error: 0.1.7 + dev: true + + /@changesets/get-dependents-graph@2.0.0: + resolution: {integrity: sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA==} + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 7.5.4 + dev: true + + /@changesets/get-github-info@0.6.0: + resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} + dependencies: + dataloader: 1.4.0 + node-fetch: 2.7.0 transitivePeerDependencies: - - '@volar/vue-language-plugin-pug' - - '@volar/vue-typescript' - - supports-color - - typescript + - encoding dev: true - /@bcoe/v8-coverage@0.2.3: - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + /@changesets/get-release-plan@4.0.0: + resolution: {integrity: sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==} + dependencies: + '@babel/runtime': 7.24.0 + '@changesets/assemble-release-plan': 6.0.0 + '@changesets/config': 3.0.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + dev: true + + /@changesets/get-version-range-type@0.4.0: + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + dev: true + + /@changesets/git@3.0.0: + resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + dependencies: + '@babel/runtime': 7.24.0 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.5 + spawndamnit: 2.0.0 + dev: true + + /@changesets/logger@0.1.0: + resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + dependencies: + chalk: 2.4.2 + dev: true + + /@changesets/parse@0.4.0: + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + dev: true + + /@changesets/pre@2.0.0: + resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + dependencies: + '@babel/runtime': 7.24.0 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + dev: true + + /@changesets/read@0.6.0: + resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + dependencies: + '@babel/runtime': 7.24.0 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + dev: true + + /@changesets/types@4.1.0: + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + dev: true + + /@changesets/types@6.0.0: + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + dev: true + + /@changesets/write@0.3.0: + resolution: {integrity: sha512-slGLb21fxZVUYbyea+94uFiD6ntQW0M2hIKNznFizDhZPDgn2c/fv1UzzlW43RVzh1BEDuIqW6hzlJ1OflNmcw==} + dependencies: + '@babel/runtime': 7.24.0 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + dev: true + + /@fastify/ajv-compiler@3.5.0: + resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + fast-uri: 2.3.0 + dev: true + + /@fastify/error@3.4.1: + resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + dev: true + + /@fastify/fast-json-stringify-compiler@4.3.0: + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + dependencies: + fast-json-stringify: 5.12.0 + dev: true + + /@fastify/formbody@7.4.0: + resolution: {integrity: sha512-H3C6h1GN56/SMrZS8N2vCT2cZr7mIHzBHzOBa5OPpjfB/D6FzP9mMpE02ZzrFX0ANeh0BAJdoXKOF2e7IbV+Og==} + dependencies: + fast-querystring: 1.1.2 + fastify-plugin: 4.5.1 + dev: true + + /@fastify/merge-json-schemas@0.1.1: + resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + dependencies: + fast-deep-equal: 3.1.3 dev: true /@istanbuljs/schema@0.1.3: @@ -116,6 +511,13 @@ packages: engines: {node: '>=8'} dev: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -132,29 +534,25 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@kitajs/html@3.0.11(@kitajs/ts-html-plugin@1.3.3): - resolution: {integrity: sha512-YaK/4LEQtWWFUg1VNDrD6OSGUiUcj1iy+lON5KnOl0Fq5EtJUB9cxGqqH4zWCKcPpx2MloXacEiWqSm4MzHxWw==} - engines: {node: '>=12'} - peerDependencies: - '@kitajs/ts-html-plugin': '>=1.2.0' + /@manypkg/find-root@1.1.0: + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@kitajs/ts-html-plugin': 1.3.3(@kitajs/html@3.0.11)(typescript@5.4.2) - csstype: 3.1.3 - dev: false + '@babel/runtime': 7.24.0 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + dev: true - /@kitajs/ts-html-plugin@1.3.3(@kitajs/html@3.0.11)(typescript@5.4.2): - resolution: {integrity: sha512-qam/xc4zgP9e/CeSlBcpmJ5BOf9jCUr7qee6KcxzuFXN8fFf8IeNph46ukrQCXk0tEiItd48BnkkL0jEAozdeg==} - hasBin: true - peerDependencies: - '@kitajs/html': ^3.0.10 - typescript: ^5.2.2 + /@manypkg/get-packages@1.1.3: + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@kitajs/html': 3.0.11(@kitajs/ts-html-plugin@1.3.3) - chalk: 4.1.2 - tslib: 2.6.2 - typescript: 5.4.2 - yargs: 17.7.2 - dev: false + '@babel/runtime': 7.24.0 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + dev: true /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -189,12 +587,199 @@ packages: tslib: 2.6.2 dev: true + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@swc-node/core@1.12.0(@swc/core@1.4.2)(@swc/types@0.1.5): + resolution: {integrity: sha512-AYrEmPL2BT46wbikHwSMR5IK98SelBEYH+ycjalUxJ5xYjEupjF8Fd+NkadKoZAzf5zDtysFKd5R1PY4QBHIiw==} + engines: {node: '>= 10'} + peerDependencies: + '@swc/core': '>= 1.3' + '@swc/types': '>= 0.1' + dependencies: + '@swc/core': 1.4.2(@swc/helpers@0.5.6) + '@swc/types': 0.1.5 + dev: true + + /@swc-node/register@1.8.0(@swc/core@1.4.2)(@swc/types@0.1.5)(typescript@5.3.3): + resolution: {integrity: sha512-8K3589HoBSmVmrEVrtr4K5sWEithpGDzcFGic81OW0A9sZY38IV5EGRODQWCk0SBDyLhaF+pid120vJAtsHo1A==} + peerDependencies: + '@swc/core': '>= 1.3' + typescript: '>= 4.3' + dependencies: + '@swc-node/core': 1.12.0(@swc/core@1.4.2)(@swc/types@0.1.5) + '@swc-node/sourcemap-support': 0.4.0 + '@swc/core': 1.4.2(@swc/helpers@0.5.6) + colorette: 2.0.20 + debug: 4.3.4 + pirates: 4.0.6 + tslib: 2.6.2 + typescript: 5.3.3 + transitivePeerDependencies: + - '@swc/types' + - supports-color + dev: true + + /@swc-node/sourcemap-support@0.4.0: + resolution: {integrity: sha512-weuRmYTO+4yOtHtPZHXlPdA1dJJJp3QOoZAFZ6uZidu992F2X5v1fQdnb26xs1o3Ex/e2sYhRyY5R6NGNuoATQ==} + dependencies: + source-map-support: 0.5.21 + tslib: 2.6.2 + dev: true + + /@swc/core-darwin-arm64@1.4.2: + resolution: {integrity: sha512-1uSdAn1MRK5C1m/TvLZ2RDvr0zLvochgrZ2xL+lRzugLlCTlSA+Q4TWtrZaOz+vnnFVliCpw7c7qu0JouhgQIw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.4.2: + resolution: {integrity: sha512-TYD28+dCQKeuxxcy7gLJUCFLqrwDZnHtC2z7cdeGfZpbI2mbfppfTf2wUPzqZk3gEC96zHd4Yr37V3Tvzar+lQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.4.2: + resolution: {integrity: sha512-Eyqipf7ZPGj0vplKHo8JUOoU1un2sg5PjJMpEesX0k+6HKE2T8pdyeyXODN0YTFqzndSa/J43EEPXm+rHAsLFQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.4.2: + resolution: {integrity: sha512-wZn02DH8VYPv3FC0ub4my52Rttsus/rFw+UUfzdb3tHMHXB66LqN+rR0ssIOZrH6K+VLN6qpTw9VizjyoH0BxA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.4.2: + resolution: {integrity: sha512-3G0D5z9hUj9bXNcwmA1eGiFTwe5rWkuL3DsoviTj73TKLpk7u64ND0XjEfO0huVv4vVu9H1jodrKb7nvln/dlw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.4.2: + resolution: {integrity: sha512-LFxn9U8cjmYHw3jrdPNqPAkBGglKE3tCZ8rA7hYyp0BFxuo7L2ZcEnPm4RFpmSCCsExFH+LEJWuMGgWERoktvg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.4.2: + resolution: {integrity: sha512-dp0fAmreeVVYTUcb4u9njTPrYzKnbIH0EhH2qvC9GOYNNREUu2GezSIDgonjOXkHiTCvopG4xU7y56XtXj4VrQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.4.2: + resolution: {integrity: sha512-HlVIiLMQkzthAdqMslQhDkoXJ5+AOLUSTV6fm6shFKZKqc/9cJvr4S8UveNERL9zUficA36yM3bbfo36McwnvQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.4.2: + resolution: {integrity: sha512-WCF8faPGjCl4oIgugkp+kL9nl3nUATlzKXCEGFowMEmVVCFM0GsqlmGdPp1pjZoWc9tpYanoXQDnp5IvlDSLhA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.4.2: + resolution: {integrity: sha512-oV71rwiSpA5xre2C5570BhCsg1HF97SNLsZ/12xv7zayGzqr3yvFALFJN8tHKpqUdCB4FGPjoP3JFdV3i+1wUw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.4.2(@swc/helpers@0.5.6): + resolution: {integrity: sha512-vWgY07R/eqj1/a0vsRKLI9o9klGZfpLNOVEnrv4nrccxBgYPjcf22IWwAoaBJ+wpA7Q4fVjCUM8lP0m01dpxcg==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.6 + '@swc/types': 0.1.5 + optionalDependencies: + '@swc/core-darwin-arm64': 1.4.2 + '@swc/core-darwin-x64': 1.4.2 + '@swc/core-linux-arm-gnueabihf': 1.4.2 + '@swc/core-linux-arm64-gnu': 1.4.2 + '@swc/core-linux-arm64-musl': 1.4.2 + '@swc/core-linux-x64-gnu': 1.4.2 + '@swc/core-linux-x64-musl': 1.4.2 + '@swc/core-win32-arm64-msvc': 1.4.2 + '@swc/core-win32-ia32-msvc': 1.4.2 + '@swc/core-win32-x64-msvc': 1.4.2 + dev: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/helpers@0.5.6: + resolution: {integrity: sha512-aYX01Ke9hunpoCexYAgQucEpARGQ5w/cqHFrIR+e9gdKb1QWTsVJuTJ2ozQzIAxLyRQe/m+2RqzkyOOGiMKRQA==} + dependencies: + tslib: 2.6.2 + dev: true + + /@swc/types@0.1.5: + resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} + dev: true + + /@tsd/typescript@5.3.3: + resolution: {integrity: sha512-CQlfzol0ldaU+ftWuG52vH29uRoKboLinLy84wS8TQOu+m+tWoaUfk4svL4ij2V8M5284KymJBlHUusKj6k34w==} + engines: {node: '>=14.17'} + dev: true + /@types/debug@4.1.9: resolution: {integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==} dependencies: '@types/ms': 0.7.31 dev: true + /@types/eslint@7.29.0: + resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==} + dependencies: + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} dev: true @@ -202,27 +787,69 @@ packages: /@types/jsdom@21.1.6: resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} dependencies: - '@types/node': 20.11.25 + '@types/node': 20.11.24 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 dev: true + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + /@types/mdast@4.0.2: resolution: {integrity: sha512-tYR83EignvhYO9iU3kDg8V28M0jqyh9zzp5GV+EO+AYnyUl3P5ltkTeJuTiFZQFz670FSb3EwT/6LQdX+UdKfw==} dependencies: '@types/unist': 3.0.1 dev: true + /@types/minimist@1.2.5: + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + dev: true + /@types/ms@0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} dev: true - /@types/node@20.11.25: - resolution: {integrity: sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==} + /@types/node@12.20.55: + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + dev: true + + /@types/node@20.11.20: + resolution: {integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/node@20.11.24: + resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} dependencies: undici-types: 5.26.5 dev: true + /@types/normalize-package-data@2.4.4: + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + dev: true + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + dev: true + + /@types/react@18.2.61: + resolution: {integrity: sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 + dev: true + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + dev: true + + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + dev: true + /@types/tough-cookie@4.0.5: resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} dev: true @@ -231,6 +858,27 @@ packages: resolution: {integrity: sha512-ue/hDUpPjC85m+PM9OQDMZr3LywT+CT6mPsQq8OJtCLiERkGRcQUFvu9XASF5XWqyZFXbf15lvb3JFJ4dRLWPg==} dev: true + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.32: + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: true + + /abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + dev: true + /agent-base@7.1.0: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} @@ -240,6 +888,31 @@ packages: - supports-color dev: true + /ajv-formats@2.1.1(ajv@8.12.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true + /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -251,45 +924,116 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - /astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-shim-unscopables: 1.0.2 + dev: true + + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + dev: true + + /arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true + /atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + dev: true + + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: true + + /avvio@8.3.0: + resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==} + dependencies: + '@fastify/error': 3.4.1 + archy: 1.0.0 + debug: 4.3.4 + fastq: 1.17.1 + transitivePeerDependencies: + - supports-color + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /benchmark@2.1.4: - resolution: {integrity: sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==} - dependencies: - lodash: 4.17.21 - platform: 1.3.6 + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true - /benny@3.7.1: - resolution: {integrity: sha512-USzYxODdVfOS7JuQq/L0naxB788dWCiUgUTxvN+WLPt/JfcDURNNj8kN/N+uK6PDvuR67/9/55cVKGPleFQINA==} - engines: {node: '>=12'} + /better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} dependencies: - '@arrows/composition': 1.2.2 - '@arrows/dispatch': 1.0.3 - '@arrows/multimethod': 1.4.1 - benchmark: 2.1.4 - common-tags: 1.8.2 - fs-extra: 10.1.0 - json2csv: 5.0.7 - kleur: 4.1.5 - log-update: 4.0.0 + is-windows: 1.0.2 dev: true /big-integer@1.6.51: @@ -322,6 +1066,23 @@ packages: fill-range: 7.0.1 dev: true + /breakword@1.0.6: + resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} + dependencies: + wcwidth: 1.0.1 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -347,23 +1108,66 @@ packages: yargs-parser: 21.1.1 dev: true + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.1 + dev: true + + /camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: false /character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} dev: true - /cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + dev: true + + /cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: - restore-cursor: 3.1.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 dev: true /cliui@8.0.1: @@ -374,15 +1178,34 @@ packages: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: true + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -390,11 +1213,6 @@ packages: delayed-stream: 1.0.0 dev: true - /commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - dev: true - /comment-parser@1.4.0: resolution: {integrity: sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==} engines: {node: '>= 12.0.0'} @@ -403,7 +1221,7 @@ packages: /common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - dev: true + dev: false /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -413,6 +1231,19 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: true + + /cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -431,7 +1262,28 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: false + + /csv-generate@3.4.3: + resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} + dev: true + + /csv-parse@4.16.3: + resolution: {integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==} + dev: true + + /csv-stringify@5.6.5: + resolution: {integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==} + dev: true + + /csv@5.5.3: + resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==} + engines: {node: '>= 0.1.90'} + dependencies: + csv-generate: 3.4.3 + csv-parse: 4.16.3 + csv-stringify: 5.6.5 + stream-transform: 2.1.3 + dev: true /data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} @@ -441,6 +1293,10 @@ packages: whatwg-url: 14.0.0 dev: true + /dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + dev: true + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -453,6 +1309,19 @@ packages: ms: 2.1.2 dev: true + /decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true @@ -481,11 +1350,35 @@ packages: titleize: 3.0.0 dev: true + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 + dev: true + + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: true + /define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} dev: true + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + dev: true + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -496,6 +1389,11 @@ packages: engines: {node: '>=6'} dev: true + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + /detect-indent@7.0.1: resolution: {integrity: sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==} engines: {node: '>=12.20'} @@ -512,6 +1410,11 @@ packages: dequal: 2.0.3 dev: true + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -519,18 +1422,159 @@ packages: path-type: 4.0.0 dev: true + /dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + dev: true + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + /enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + dev: true + /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} dev: true + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.22.5: + resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.1 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.0 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.5 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.14 + dev: true + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: true + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: true + + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.1 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.1 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /eslint-formatter-pretty@4.1.0: + resolution: {integrity: sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==} + engines: {node: '>=10'} + dependencies: + '@types/eslint': 7.29.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + eslint-rule-docs: 1.1.235 + log-symbols: 4.1.0 + plur: 4.0.0 + string-width: 4.2.3 + supports-hyperlinks: 2.3.0 + dev: true + + /eslint-rule-docs@1.1.235: + resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==} + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: true + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -561,10 +1605,35 @@ packages: strip-final-newline: 3.0.0 dev: true + /extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + dev: true + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + dev: true + + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true + /fast-defer@1.1.8: + resolution: {integrity: sha512-lEJeOH5VL5R09j6AA0D4Uvq7AgsHw0dAImQQ+F3iSyHZuAxyQfWobsagGpTcOPvJr3urmKRHrs+Gs9hV+/Qm/Q==} + dev: true + /fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} @@ -576,12 +1645,71 @@ packages: micromatch: 4.0.5 dev: true + /fast-json-stringify@5.12.0: + resolution: {integrity: sha512-7Nnm9UPa7SfHRbHVA1kJQrGXCRzB7LMlAAqHXQFkEQqueJm1V8owm0FsE/2Do55/4CcdhwiLQERaKomOnKQkyA==} + dependencies: + '@fastify/merge-json-schemas': 0.1.1 + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + fast-deep-equal: 3.1.3 + fast-uri: 2.3.0 + json-schema-ref-resolver: 1.0.1 + rfdc: 1.3.1 + dev: true + + /fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + dependencies: + fast-decode-uri-component: 1.0.1 + dev: true + + /fast-redact@3.3.0: + resolution: {integrity: sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==} + engines: {node: '>=6'} + dev: true + + /fast-uri@2.3.0: + resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} + dev: true + + /fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + + /fastify@4.26.1: + resolution: {integrity: sha512-tznA/G55dsxzM5XChBfcvVSloG2ejeeotfPPJSFaWmHyCDVGMpvf3nRNbsCb/JTBF9RmQFBfuujWt3Nphjesng==} + dependencies: + '@fastify/ajv-compiler': 3.5.0 + '@fastify/error': 3.4.1 + '@fastify/fast-json-stringify-compiler': 4.3.0 + abstract-logging: 2.0.1 + avvio: 8.3.0 + fast-content-type-parse: 1.1.0 + fast-json-stringify: 5.12.0 + find-my-way: 8.1.0 + light-my-request: 5.11.1 + pino: 8.19.0 + process-warning: 3.0.0 + proxy-addr: 2.0.7 + rfdc: 1.3.1 + secure-json-parse: 2.7.0 + semver: 7.5.4 + toad-cache: 3.7.0 + transitivePeerDependencies: + - supports-color + dev: true + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 dev: true + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + dev: true + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -589,6 +1717,23 @@ packages: to-regex-range: 5.0.1 dev: true + /find-my-way@8.1.0: + resolution: {integrity: sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==} + engines: {node: '>=14'} + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 2.0.0 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -597,6 +1742,19 @@ packages: path-exists: 4.0.0 dev: true + /find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + dependencies: + micromatch: 4.0.5 + pkg-dir: 4.2.0 + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -614,23 +1772,66 @@ packages: mime-types: 2.1.35 dev: true - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: true + + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 + jsonfile: 4.0.0 + universalify: 0.1.2 dev: true /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.1 + dev: true + /get-stdin@9.0.0: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} @@ -641,10 +1842,19 @@ packages: engines: {node: '>=10'} dev: true + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + dev: true + /ghtml@1.0.1: resolution: {integrity: sha512-UazsDitjsbxCNksuUINln98LE+YNau/AaLJP752+Hac1zhgnwmwPILKSVaiuJ/ET0XvyQ/j8eEkUXUM10YHvMA==} engines: {node: '>=18'} - dev: true + dev: false /git-hooks-list@3.1.0: resolution: {integrity: sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==} @@ -668,6 +1878,25 @@ packages: path-is-absolute: 1.0.1 dev: true + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + /globby@13.2.2: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -679,14 +1908,79 @@ packages: slash: 4.0.0 dev: true + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + dev: true + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /hasown@2.0.1: + resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: true + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + /html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -718,37 +2012,126 @@ packages: - supports-color dev: true + /human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + dev: true + /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} dev: true - /human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /husky@9.0.11: + resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} + engines: {node: '>=18'} + hasBin: true + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + hasown: 2.0.1 + side-channel: 1.0.6 + dev: true + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: true + + /irregular-plurals@3.5.0: + resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} + engines: {node: '>=8'} + dev: true + + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 dev: true - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} dependencies: - safer-buffer: 2.1.2 + call-bind: 1.0.7 + has-tostringtag: 1.0.2 dev: true - /ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} - engines: {node: '>= 4'} + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} dev: true - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - once: 1.4.0 - wrappy: 1.0.2 + hasown: 2.0.1 dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 dev: true /is-docker@2.2.1: @@ -787,11 +2170,28 @@ packages: is-docker: 3.0.0 dev: true + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true + /is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + /is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -801,6 +2201,21 @@ packages: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: true + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -811,6 +2226,50 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + dependencies: + better-path-resolve: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.14 + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.7 + dev: true + + /is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true + /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -818,6 +2277,10 @@ packages: is-docker: 2.2.1 dev: true + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -844,8 +2307,30 @@ packages: istanbul-lib-report: 3.0.1 dev: true + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 dev: true /jsdom@24.0.0: @@ -884,28 +2369,29 @@ packages: - utf-8-validate dev: true - /json2csv@5.0.7: - resolution: {integrity: sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA==} - engines: {node: '>= 10', npm: '>= 6.13.0'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - hasBin: true - dependencies: - commander: 6.2.1 - jsonparse: 1.3.1 - lodash.get: 4.4.2 + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + /json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} dependencies: - universalify: 2.0.0 + fast-deep-equal: 3.1.3 + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 dev: true - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} dev: true /kleur@4.1.5: @@ -913,6 +2399,35 @@ packages: engines: {node: '>=6'} dev: true + /light-my-request@5.11.1: + resolution: {integrity: sha512-KXAh2m6VRlkWCk2KfmHE7tLBXKh30JE0tXUJY4dNxje4oLmPKUqlUfImiEQZLphx+Z9KTQcVv4DjGnJxkVOIbA==} + dependencies: + cookie: 0.6.0 + process-warning: 2.3.2 + set-cookie-parser: 2.6.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -920,22 +2435,16 @@ packages: p-locate: 5.0.0 dev: true - /lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - dev: true - - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + /lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} dev: true - /log-update@4.0.0: - resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} dependencies: - ansi-escapes: 4.3.2 - cli-cursor: 3.1.0 - slice-ansi: 4.0.0 - wrap-ansi: 6.2.0 + chalk: 4.1.2 + is-unicode-supported: 0.1.0 dev: true /loose-envify@1.4.0: @@ -943,6 +2452,13 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 + dev: false + + /lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 dev: true /lru-cache@6.0.0: @@ -959,6 +2475,16 @@ packages: semver: 7.5.4 dev: true + /map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: true + /mdast-util-from-markdown@2.0.0: resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} dependencies: @@ -984,6 +2510,41 @@ packages: '@types/mdast': 4.0.2 dev: true + /meow@6.1.1: + resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} + engines: {node: '>=8'} + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 2.5.0 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.13.1 + yargs-parser: 18.1.3 + dev: true + + /meow@9.0.0: + resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} + engines: {node: '>=10'} + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize: 1.2.0 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + dev: true + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -1198,20 +2759,70 @@ packages: engines: {node: '>=12'} dev: true + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true + /minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + /mitata@0.1.11: resolution: {integrity: sha512-cs6FiWcnRxn7atVumm8wA8R70XCDmMXgVgb/qWUSjr5dwuIBr7zC+22mbGYPlbyFixlIOjuP//A0e72Q1ZoGDw==} + dev: false + + /mixme@0.5.10: + resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} + engines: {node: '>= 8.0.0'} dev: true /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.13.1 + semver: 7.5.4 + validate-npm-package-license: 3.0.4 + dev: true + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -1230,6 +2841,30 @@ packages: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} dev: true + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + dev: true + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -1260,6 +2895,29 @@ packages: is-wsl: 2.2.0 dev: true + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + dev: true + + /p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + dependencies: + p-map: 2.1.0 + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1267,6 +2925,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -1274,6 +2939,26 @@ packages: p-limit: 3.1.0 dev: true + /p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.23.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + /parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} dependencies: @@ -1300,6 +2985,10 @@ packages: engines: {node: '>=12'} dev: true + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -1314,8 +3003,71 @@ packages: engines: {node: '>=8.6'} dev: true - /platform@1.3.6: - resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true + + /pino-abstract-transport@1.1.0: + resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + dependencies: + readable-stream: 4.5.2 + split2: 4.2.0 + dev: true + + /pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + dev: true + + /pino@8.19.0: + resolution: {integrity: sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==} + hasBin: true + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.3.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pino-std-serializers: 6.2.2 + process-warning: 3.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.4.3 + sonic-boom: 3.8.0 + thread-stream: 2.4.1 + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /plur@4.0.0: + resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==} + engines: {node: '>=10'} + dependencies: + irregular-plurals: 3.5.0 + dev: true + + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: true + + /preferred-pm@3.1.3: + resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} + engines: {node: '>=10'} + dependencies: + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.0.0 dev: true /prettier-plugin-jsdoc@1.1.1(prettier@3.2.5): @@ -1332,7 +3084,7 @@ packages: - supports-color dev: true - /prettier-plugin-organize-imports@3.2.3(prettier@3.2.5)(typescript@5.4.2): + /prettier-plugin-organize-imports@3.2.3(prettier@3.2.5)(typescript@5.3.3): resolution: {integrity: sha512-KFvk8C/zGyvUaE3RvxN2MhCLwzV6OBbFSkwZ2OamCrs9ZY4i5L77jQ/w4UmUr+lqX8qbaqVq6bZZkApn+IgJSg==} peerDependencies: '@volar/vue-language-plugin-pug': ^1.0.4 @@ -1346,7 +3098,7 @@ packages: optional: true dependencies: prettier: 3.2.5 - typescript: 5.4.2 + typescript: 5.3.3 dev: true /prettier-plugin-packagejson@2.4.6(prettier@3.2.5): @@ -1362,12 +3114,52 @@ packages: synckit: 0.8.5 dev: true + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} hasBin: true dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + dev: true + + /process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + dev: true + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: true + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: true + + /pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true + /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: true @@ -1385,37 +3177,137 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 - dependencies: - loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 + /quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + dev: true + + /quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + dev: true + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + dev: true + + /readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + dev: true + + /real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + dev: true + + /redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} dev: true - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} dependencies: - loose-envify: 1.4.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 dev: true /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + /requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: true - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} dev: true /reusify@1.0.4: @@ -1423,6 +3315,10 @@ packages: engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true + /rfdc@1.3.1: + resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} + dev: true + /rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} dev: true @@ -1440,6 +3336,40 @@ packages: queue-microtask: 1.2.3 dev: true + /safe-array-concat@1.1.0: + resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + dev: true + + /safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + dependencies: + ret: 0.2.2 + dev: true + + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: true + /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true @@ -1455,6 +3385,15 @@ packages: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 + dev: false + + /secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + dev: true + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true dev: true /semver@7.5.4: @@ -1465,6 +3404,43 @@ packages: lru-cache: 6.0.0 dev: true + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true + + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: true + + /set-function-length@1.2.1: + resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + dev: true + + /set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + dev: true + + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1472,11 +3448,26 @@ packages: shebang-regex: 3.0.0 dev: true + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: true + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} dev: true + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -1486,18 +3477,33 @@ packages: engines: {node: '>=14'} dev: true + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + /slash@4.0.0: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} dev: true - /slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} + /smartwrap@2.0.2: + resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} + engines: {node: '>=6'} + hasBin: true dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 + array.prototype.flat: 1.3.2 + breakword: 1.0.6 + grapheme-splitter: 1.0.4 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + yargs: 15.4.1 + dev: true + + /sonic-boom@3.8.0: + resolution: {integrity: sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==} + dependencies: + atomic-sleep: 1.0.0 dev: true /sort-object-keys@1.1.3: @@ -1517,6 +3523,62 @@ packages: sort-object-keys: 1.1.3 dev: true + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + dependencies: + cross-spawn: 5.1.0 + signal-exit: 3.0.7 + dev: true + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.17 + dev: true + + /spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.17 + dev: true + + /spdx-license-ids@3.0.17: + resolution: {integrity: sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==} + dev: true + + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stream-transform@2.1.3: + resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} + dependencies: + mixme: 0.5.10 + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1525,12 +3587,48 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.22.5 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -1541,12 +3639,39 @@ packages: engines: {node: '>=12'} dev: true + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 + /supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + /symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true @@ -1559,6 +3684,11 @@ packages: tslib: 2.6.2 dev: true + /term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true + /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -1568,11 +3698,24 @@ packages: minimatch: 3.1.2 dev: true + /thread-stream@2.4.1: + resolution: {integrity: sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==} + dependencies: + real-require: 0.2.0 + dev: true + /titleize@3.0.0: resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} engines: {node: '>=12'} dev: true + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1580,6 +3723,11 @@ packages: is-number: 7.0.0 dev: true + /toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + dev: true + /tough-cookie@4.1.3: resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} engines: {node: '>=6'} @@ -1590,6 +3738,10 @@ packages: url-parse: 1.5.10 dev: true + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: true + /tr46@5.0.0: resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} @@ -1597,24 +3749,130 @@ packages: punycode: 2.3.1 dev: true + /trim-newlines@3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + dev: true + + /tsd@0.30.7: + resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + '@tsd/typescript': 5.3.3 + eslint-formatter-pretty: 4.1.0 + globby: 11.1.0 + jest-diff: 29.7.0 + meow: 9.0.0 + path-exists: 4.0.0 + read-pkg-up: 7.0.1 + dev: true + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + /tty-table@4.2.3: + resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + csv: 5.5.3 + kleur: 4.1.5 + smartwrap: 2.0.2 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + yargs: 17.7.2 + dev: true + + /type-fest@0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + dev: true + /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} dev: true + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-length@1.0.5: + resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + dev: true + /typed-html@3.0.1: resolution: {integrity: sha512-JKCM9zTfPDuPqQqdGZBWSEiItShliKkBFg5c6yOR8zth43v763XkAzTWaOlVqc0Y6p9ee8AaAbipGfUnCsYZUA==} engines: {node: '>=12'} - dev: true + dev: false - /typescript@5.4.2: - resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} hasBin: true + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true @@ -1625,14 +3883,14 @@ packages: '@types/unist': 3.0.1 dev: true - /universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} dev: true - /universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} - engines: {node: '>= 10.0.0'} + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} dev: true /untildify@4.0.0: @@ -1640,6 +3898,12 @@ packages: engines: {node: '>=8'} dev: true + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + /url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} dependencies: @@ -1656,6 +3920,13 @@ packages: convert-source-map: 1.9.0 dev: true + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + /w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -1663,6 +3934,16 @@ packages: xml-name-validator: 5.0.0 dev: true + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true + /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -1688,6 +3969,53 @@ packages: webidl-conversions: 7.0.0 dev: true + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + dev: true + + /which-pm@2.0.0: + resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} + engines: {node: '>=8.15'} + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + dev: true + + /which-typed-array@1.1.14: + resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: true + + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1739,18 +4067,56 @@ packages: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} dev: true + /y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + /yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + /yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: true + /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -1767,3 +4133,19 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + file:packages/ts-html-plugin(@kitajs/html@packages+html)(typescript@5.3.3): + resolution: {directory: packages/ts-html-plugin, type: directory} + id: file:packages/ts-html-plugin + name: '@kitajs/ts-html-plugin' + hasBin: true + peerDependencies: + '@kitajs/html': workspace:^ + typescript: ^5.3.3 + dependencies: + '@kitajs/html': link:packages/html + chalk: 4.1.2 + tslib: 2.6.2 + typescript: 5.3.3 + yargs: 17.7.2 + dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 000000000..94c9de2fd --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'packages/*' + - 'benchmarks/*' diff --git a/tsconfig.benchmark.json b/tsconfig.benchmark.json deleted file mode 100644 index a97171bf1..000000000 --- a/tsconfig.benchmark.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "dist/benchmark", - "module": "ESNext", - "target": "ESNext", - "allowArbitraryExtensions": true, - "skipDefaultLibCheck": true, - "skipLibCheck": true - }, - "include": ["benchmark"], - "exclude": [] -} diff --git a/tsconfig.json b/tsconfig.json index 316463811..fb25a9637 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,8 @@ { "compilerOptions": { "target": "ESNext", - "jsx": "react", - "jsxFactory": "Html.createElement", - "jsxFragmentFactory": "Html.Fragment", + "jsx": "react-jsx", + "jsxImportSource": "@kitajs/html", "plugins": [{ "name": "@kitajs/ts-html-plugin" }], "module": "CommonJS", "moduleResolution": "node", @@ -35,5 +34,5 @@ "skipDefaultLibCheck": true }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.d.ts", "node_modules/csstype"], - "exclude": ["node_modules", "dist", "coverage", "benchmark"] + "exclude": ["node_modules", "dist", "coverage", "benchmarks"] }