diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
new file mode 100644
index 0000000..9915e21
--- /dev/null
+++ b/.github/workflows/npm-publish.yml
@@ -0,0 +1,120 @@
+name: Publish NPM Package
+
+on:
+ push:
+ branches:
+ - main
+ - beta
+ - 'dev-*'
+ pull_request:
+ branches:
+ - main
+ - beta
+ - 'dev-*'
+
+env:
+ BUN_VERSION: 1.2.21
+ NODE_VERSION: 22
+
+jobs:
+ version:
+ runs-on: ubuntu-latest
+ outputs:
+ npm_tag: ${{ steps.set-tag.outputs.npm_tag }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Determine NPM tag
+ id: set-tag
+ run: |
+ VERSION=$(node -p "require('./package.json').version")
+
+ if [[ $VERSION == *"-beta."* ]]; then
+ NPM_TAG="beta"
+ elif [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ NPM_TAG="latest"
+ else
+ NPM_TAG="dev"
+ fi
+
+ echo "Detected version: $VERSION"
+ echo "NPM tag: $NPM_TAG"
+ echo "npm_tag=$NPM_TAG" >> $GITHUB_OUTPUT
+
+ - name: Validate branch and tag
+ if: |
+ (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta'))
+ || (github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'beta'))
+ run: |
+ if [[ "${{ github.event_name }}" == "push" ]]; then
+ BRANCH=${GITHUB_REF#refs/heads/}
+ else
+ BRANCH=${{ github.base_ref }}
+ fi
+
+ NPM_TAG=${{ steps.set-tag.outputs.npm_tag }}
+
+ if [[ $BRANCH == "main" && $NPM_TAG != "latest" ]] || \
+ [[ $BRANCH == "beta" && $NPM_TAG != "beta" ]]; then
+ echo "Error: Version tag $NPM_TAG doesn't match branch $BRANCH"
+ exit 1
+ fi
+
+ check:
+ needs: [version]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: ${{ env.BUN_VERSION }}
+
+ - name: Install dependencies
+ run: bun install --frozen-lockfile
+
+ - name: Lint code
+ run: bun run lint
+
+ - name: Test code
+ run: bun run test
+
+ publish:
+ needs: [version, check]
+ if: |
+ github.event_name == 'push' &&
+ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta')
+ permissions:
+ contents: read
+ id-token: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+ registry-url: https://registry.npmjs.org/
+
+ - name: Set up Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: ${{ env.BUN_VERSION }}
+
+ - name: Install dependencies
+ run: bun install --frozen-lockfile
+
+ - name: Build code
+ run: bun run build
+
+ - name: Publish to NPM
+ run: npm publish --access public --provenance --tag ${{ needs.version.outputs.npm_tag }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - uses: kirick13/notify-action@v1
+ with:
+ telegram-bot-token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
+ telegram-chat-id: ${{ secrets.TELEGRAM_CHAT_ID }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2e217a0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+.env
+*.env
+node_modules
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..83f941b
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,36 @@
+# ignore source files of the Typescript project
+src/
+
+# ignore tests
+**/*.test.*
+**/*.test.d.ts
+test/
+test-d/
+
+# ignore configs
+.luarc.json
+biome.json
+# these names cannot be declared using {js,mjs,cjs} syntax
+# is this case rule n/no-unpublished-import yells at me
+eslint.config.js
+eslint.config.mjs
+eslint.config.cjs
+tsconfig.json
+tsconfig.*.json
+vitest.config.js
+vitest.config.ts
+
+# ignore lockfiles
+bun.lock
+bun.lockb
+package-lock.json
+pnpm-lock.yaml
+yarn.lock
+
+# ignore repo configs
+.github/
+.gitea/
+
+# ignore IDE files
+.idea
+.nova
\ No newline at end of file
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..7c73db6
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "extends": [
+ "./node_modules/@kirick/lint/configs/oxlint/correctness.jsonc",
+ "./node_modules/@kirick/lint/configs/oxlint/pedantic.jsonc",
+ "./node_modules/@kirick/lint/configs/oxlint/perf.jsonc",
+ "./node_modules/@kirick/lint/configs/oxlint/restriction.jsonc",
+ "./node_modules/@kirick/lint/configs/oxlint/style.jsonc",
+ "./node_modules/@kirick/lint/configs/oxlint/suspicious.jsonc"
+ ],
+ "ignorePatterns": ["dist"]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..99357f2
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "biomejs.biome",
+ "editor.codeActionsOnSave": {
+ "source.fixAll.biome": "explicit",
+ "source.organizeImports.biome": "explicit"
+ }
+}
diff --git a/.zed/settings.json b/.zed/settings.json
new file mode 100644
index 0000000..66d29b4
--- /dev/null
+++ b/.zed/settings.json
@@ -0,0 +1,72 @@
+{
+ "languages": {
+ "JavaScript": {
+ "format_on_save": "on",
+ "formatter": [
+ { "language_server": { "name": "biome" } },
+ { "code_action": "source.organizeImports.biome" },
+ { "code_action": "source.fixAll.biome" }
+ ]
+ },
+ "TypeScript": {
+ "format_on_save": "on",
+ "formatter": [
+ { "language_server": { "name": "biome" } },
+ { "code_action": "source.organizeImports.biome" },
+ { "code_action": "source.fixAll.biome" }
+ ]
+ },
+ "TSX": {
+ "format_on_save": "on",
+ "formatter": [
+ { "language_server": { "name": "biome" } },
+ { "code_action": "source.organizeImports.biome" },
+ { "code_action": "source.fixAll.biome" }
+ ]
+ },
+ "JSON": {
+ "format_on_save": "on",
+ "formatter": [{ "language_server": { "name": "biome" } }]
+ },
+ "JSONC": {
+ "format_on_save": "on",
+ "formatter": [{ "language_server": { "name": "biome" } }]
+ },
+ "CSS": {
+ "format_on_save": "on",
+ "formatter": [{ "language_server": { "name": "biome" } }]
+ },
+ "HTML": {
+ "format_on_save": "on",
+ "formatter": [
+ {
+ "external": {
+ "command": "bunx",
+ "arguments": [
+ "--bun",
+ "prettier",
+ "--stdin-filepath",
+ "{buffer_path}"
+ ]
+ }
+ }
+ ]
+ },
+ "Vue.js": {
+ "format_on_save": "on",
+ "formatter": [
+ {
+ "external": {
+ "command": "bunx",
+ "arguments": [
+ "--bun",
+ "prettier",
+ "--stdin-filepath",
+ "{buffer_path}"
+ ]
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/README.md b/README.md
index 4c56471..1feb9e2 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,305 @@
-# core
\ No newline at end of file
+# HyperAPI Core
+
+[](https://www.npmjs.com/package/@hyperapi/core)
+[](https://github.com/hyperapi/core/blob/main/LICENSE)
+
+A powerful, type-safe foundation framework for building APIs with minimal boilerplate. HyperAPI Core provides routing, standardized API method modules, and hooks, while leaving the connection to the outside world to be handled by drivers.
+
+## Features
+
+- 🔒 **Type-safe API development** - Get full TypeScript support and inference
+- 🧩 **File-based routing** - Automatically generate API endpoints from your file structure
+- 🚀 **Driver-based architecture** - Easily adapt to different environments (HTTP, WebSockets, etc.)
+- 🪝 **Middleware hooks** - Extensible system for authentication, logging, and more
+
+## Installation
+
+```bash
+bun i @hyperapi/core
+# or with pnpm
+pnpm add @hyperapi/core
+# or with npm
+npm install @hyperapi/core
+```
+
+## Quick Start
+
+> [!NOTE]
+> HyperAPI Core by itself cannot serve requests: you must use a driver package to connect with the outside world. HyperAPI can work with any protocol through its driver system: [HTTP](https://github.com/hyperapi/driver-http-bun), WebSocket, [Tasq](https://github.com/kirick-ts/tasq), [IPC](https://github.com/hyperapi/driver-ipc), or any custom protocol you create a driver for.
+
+### 1. Set up your API structure
+
+Create a directory to hold your API methods (default: `hyper-api` in your project root):
+
+```
+my-project/
+├── hyper-api/
+│ ├── users.get.ts
+│ ├── users.post.ts
+│ └── products/
+│ ├── [id].get.ts
+│ └── search.get.ts
+├── index.ts
+└── package.json
+```
+
+HyperAPI uses file names to determine routes and HTTP methods:
+
+#### Static Routes
+
+File name will require an exact match. For example:
+
+| File name | Route pattern | Matched requests |
+| - | - | - |
+| `/users.ts` | `/users` | `GET /users` |
+
+#### Index Routes
+
+Files named as `index.ts` do not add `index` to the route.
+
+| File name | Route pattern | Matched requests |
+| - | - | - |
+| `/index.ts` | `/` | `GET /` |
+| `/account/index.ts` | `/account` | `GET /account` |
+
+#### Route parameters
+
+Wrap any route parameter with `[]` to capture it.
+
+| File name | Route pattern | Matched requests |
+| - | - | - |
+| `/users/[id].ts` | `/users/:id` | `GET /users/123` with `{ id: "123" }`
`GET /users/foo` with `{ id: "foo" }` |
+
+#### Optional route parameters
+
+Make a route parameter optional by wrapping it name with `[[]]` (double brackets).
+
+| File name | Route pattern | Matched requests |
+| - | - | - |
+| `/users/[[id]].ts` | `/users/:id?` | `GET /users` with `{}`
`GET /users/123` with `{ id: "123" }`
`GET /users/foo` with `{ id: "foo" }` |
+
+You can mix parameters (both required and optional) in a single route segment:
+
+| File name | Route pattern | Matched requests |
+| - | - | - |
+| `/files/[name].[[ext]].ts` | `/files/:name.:ext?` | `GET /files/image.png` with `{ name: "image", ext: "png" }`
`GET /files/README` with `{ name: "README" }` |
+
+#### Catch-all parameters
+
+Route parameter names as `[...name]` consumes all remaining path segments. This route can only be used in a file name.
+
+| File name | Route pattern | Matched requests | Unmatched requests |
+| - | - | - | - |
+| `/docs/[...path].ts` | `/docs/:path+` | `GET /docs/foo` with `{ path: "foo" }`
`GET /docs/a/b/c` with `{ path: "a/b/c" }` | `GET /docs` |
+
+#### Optional catch-all parameters
+
+Route parameter names as `[[...name]]` consumes all remaining path segments, but also matches routes with no segments. This route can only be used in a file name.
+
+| File name | Route pattern | Matched requests |
+| - | - | - |
+| `/docs/[[...path]].ts` | `/docs/:path+` | `GET /docs` with `{}`
`GET /docs/foo` with `{ path: "foo" }`
`GET /docs/a/b/c` with `{ path: "a/b/c" }` |
+
+#### HTTP methods
+
+Add HTTP method before an extension to make route match only given HTTP method. By default, route will serve any incoming HTTP method.
+
+| File name | Route pattern | Matched requests | Unmatched requests |
+| - | - | - | - |
+| `/user/[id].get.ts` | `/user/:id` | `GET /user/1` with `{ id: "1" }` | `POST /user/1`
`DELETE /user/1` |
+
+But you can not use HTTP method in the filename if there is no name for the route. If you want to serve `POST /account`, create file `/account.post.ts` or `account/index.post.ts`, not just `account/post.ts`.
+
+### 2. Initialize HyperAPI
+
+```typescript
+import { HyperAPI } from '@hyperapi/core';
+import { SomeHttpDriver } from '@hyperapi/some-http-driver'; // fictional driver
+
+// Create a driver instance
+const driver = new SomeHttpDriver({ port: 3000 });
+
+// Initialize HyperAPI with the driver and export it
+export const hyperApi = new HyperAPI({
+ driver,
+ // Optional: custom root path for API methods
+ // root: path.join(import.meta.dir, 'api')
+});
+
+console.log('API server running on http://localhost:3000');
+```
+
+### 3. Create your API handlers
+
+Example of a basic endpoint (`hyper-api/hello.get.ts`):
+
+```typescript
+import * as v from 'valibot';
+// import HyperAPI instance you created
+import { hyperApi } from '../main.js';
+
+// Define input validation
+function params>(schema: S) {
+ return (request) => {
+ return { args: v.parse(schema, request.args) };
+ };
+}
+
+// Define your handler function
+export default hyperApi.module()
+ .use(params(
+ v.strictObject({
+ name: v.string('Name is required'),
+ }),
+ ))
+ .action((request) => {
+ // request.args now typed as { name: string }
+ return {
+ message: `Hello, ${request.args.name}!`,
+ timestamp: new Date().toISOString()
+ };
+ });
+```
+
+## Request Pipeline
+
+HyperAPI Core processes requests through a well-defined sequence of steps:
+
+1. *Driver* creates a [request](src/request.ts#L6) and passes it to the *Core*
+2. *Core* runs all registered `onBeforeRouter` hooks with request it received from the *Driver*
+3. *Core* uses a file router to match the request path and method to a module file
+ - If no match is found, a [`HyperAPIUnknownMethodError`](src/api-errors.ts#L36) is thrown
+4. *Core* merges arguments received from the driver with arguments extracted from the request path by the file router
+5. *Core* imports the matched module file dynamically and runs it
+6. *Core* executes all registered `onResponse` hooks
+7. Finally, *Core* passes the response back to the *Driver*, which sends it to the client.
+
+If an error occurs at any steps from 2 to 5, the pipeline short-circuits to the step 6. Before executing step 6, *Core* sets [`HyperAPIInternalError`](src/api-errors.ts#L18) as a response, if error thrown is not instance of `HyperAPIError`.
+
+## Using plugins before executing the module
+
+HyperAPI module allows you to use plugins (methods `use` and `set`) before executing the main code (method `action`). We described above how you can validate arguments using `valibot` and `use`.
+
+Properties returned from method `use` will be added to the request object.
+
+```typescript
+import { hyperApi } from '../somewhere.js';
+
+export default hyperApi.module()
+ .use((request) => {
+ return { foo: 1 };
+ })
+ .action((request) => {
+ // request now has property foo: number
+ return {};
+ });
+```
+
+If you want to add just one property, you can use the `set` method:
+
+```typescript
+import { hyperApi } from '../somewhere.js';
+
+export default hyperApi.module()
+ .set('foo', 1)
+ .action((request) => {
+ // request now has property foo: number
+ return {};
+ });
+```
+
+Obviously, you can `use` after `action` to do some job. Value returned from `action` is located in the `response` property of the request object.
+
+```typescript
+import { hyperApi } from '../somewhere.js';
+
+export default hyperApi.module()
+ .action((request) => {
+ return { foo: 1 };
+ })
+ .use((request) => {
+ // request now has property response: { foo: number }
+ console.log('use after action', request.response);
+ });
+```
+
+## Hooks
+
+HyperAPI provides several hooks for extending functionality at different stages of the request pipeline.
+
+### `beforeRouter` hook
+
+Executed after path normalization but before route matching. Use for logging, request inspection, or early validation. If this hook returns an object, it will be merged with the request object. However, extra properties will be visible only to the next hooks, not the API module.
+
+These hooks are executed in order of their definition, allowing you to extend request object multiple times.
+
+```typescript
+hyperApi
+ .onBeforeRouter((request) => {
+ console.log(`Incoming request: ${request.method} ${request.path}`);
+ return { started_at: Date.now() };
+ })
+ .onBeforeRouter((request) => {
+ // request has "started_at" property
+ if (isRateLimited(request)) {
+ throw new HyperAPIRateLimitError();
+ }
+ });
+```
+
+### `onResponse` hook
+
+Executed before returning response to driver. Multiple hooks will be executed in parallel.
+
+This hook has access to request and response returned from API method module. Errors thrown from this hook will not change the response.
+
+```typescript
+hyperApi.onResponse((request) => {
+ // request still has "started_at" property added by onBeforeRouter hook
+ // Useful for metrics, logging, and response modification
+ const duration = Date.now() - request.started_at;
+ console.log(`${request.method} ${request.path} completed in ${duration}ms`);
+ // also, request has "response" property
+ console.log('response =', request.response);
+});
+```
+
+## Error Handling
+
+HyperAPI provides built-in error type [`HyperAPIError`](src/error.ts) for standardized error handling. If you want to return an error from your module, you can throw an instance of `HyperAPIError` or its subclasses.
+
+```typescript
+import { HyperAPIError } from '@hyperapi/core';
+
+// for example, you create custom error for limiting access to some API methods
+class HyperAPIAccessDeniedError extends HyperAPIError {
+ // mandatory: specify numeric API code greater than 100
+ override code = 101;
+ // mandatory: specify text description
+ override description = 'Access to this API method is denied';
+ // optional: specify object with additional information to return
+ override data: { foo: 'bar' };
+ // optional: specify HTTP status code
+ override httpStatus = 403; // Forbidden
+ // optional: specify HTTP headers
+ override httpHeaders = {
+ 'X-Access-Denied': '1',
+ };
+}
+```
+
+Now you can throw this error from your module or hooks. Drivers will automatically convert it to a proper response based on their protocols.
+
+HyperAPI provides several [built-in error classes](src/api-errors.ts) for common scenarios like unauthorized access, invalid parameters, and internal errors. You can import them right from the package.
+
+## Custom Drivers
+
+HyperAPI is designed to work with any transport layer or protocol through drivers. This separation of concerns allows you to implement your API once and use it across multiple protocols without changing your business logic.
+
+## TypeScript Support
+
+HyperAPI is built with TypeScript and provides excellent type inference.
+
+## Contributing
+
+Issues and pull requests are welcome at [our GitHub repository](https://github.com/hyperapi/core).
diff --git a/biome.json b/biome.json
new file mode 100644
index 0000000..3340c42
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,50 @@
+{
+ "$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
+ "vcs": {
+ "enabled": false,
+ "clientKind": "git",
+ "useIgnoreFile": false
+ },
+ "files": {
+ "ignoreUnknown": false,
+ "includes": ["**", "!**/dist/**", "!**/*.html", "!**/*.ejs", "!**/*.vue"]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "tab",
+ "bracketSpacing": true
+ },
+ "linter": {
+ "enabled": false
+ },
+ "assist": {
+ "actions": {
+ "source": {
+ "organizeImports": "on"
+ }
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "single",
+ "operatorLinebreak": "before"
+ }
+ },
+ "css": {
+ "parser": {
+ "tailwindDirectives": true
+ },
+ "formatter": {
+ "enabled": true
+ }
+ },
+ "html": {
+ "experimentalFullSupportEnabled": true,
+ "parser": {
+ "interpolation": true
+ },
+ "formatter": {
+ "enabled": true
+ }
+ }
+}
diff --git a/bun.lock b/bun.lock
new file mode 100644
index 0000000..da4546b
--- /dev/null
+++ b/bun.lock
@@ -0,0 +1,815 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "@hyperapi/core",
+ "dependencies": {
+ "itty-router": "5.0.22",
+ "neoevents": "0.3.0",
+ "type-fest": "4.41.0",
+ },
+ "devDependencies": {
+ "@biomejs/biome": "2.3.1",
+ "@kirick/lint": "0.2.12",
+ "@types/bun": "1.3.1",
+ "eslint": "9.38.0",
+ "oxlint": "1.24.0",
+ "publint": "0.3.15",
+ "tsdown": "0.15.12",
+ "typescript": "5.9.3",
+ "unplugin-unused": "0.5.4",
+ "valibot": "1.1.0",
+ "vitest": "4.0.4",
+ },
+ },
+ },
+ "packages": {
+ "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+
+ "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+
+ "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+
+ "@biomejs/biome": ["@biomejs/biome@2.3.1", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.1", "@biomejs/cli-darwin-x64": "2.3.1", "@biomejs/cli-linux-arm64": "2.3.1", "@biomejs/cli-linux-arm64-musl": "2.3.1", "@biomejs/cli-linux-x64": "2.3.1", "@biomejs/cli-linux-x64-musl": "2.3.1", "@biomejs/cli-win32-arm64": "2.3.1", "@biomejs/cli-win32-x64": "2.3.1" }, "bin": { "biome": "bin/biome" } }, "sha512-A29evf1R72V5bo4o2EPxYMm5mtyGvzp2g+biZvRFx29nWebGyyeOSsDWGx3tuNNMFRepGwxmA9ZQ15mzfabK2w=="],
+
+ "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ombSf3MnTUueiYGN1SeI9tBCsDUhpWzOwS63Dove42osNh0PfE1cUtHFx6eZ1+MYCCLwXzlFlYFdrJ+U7h6LcA=="],
+
+ "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-pcOfwyoQkrkbGvXxRvZNe5qgD797IowpJPovPX5biPk2FwMEV+INZqfCaz4G5bVq9hYnjwhRMamg11U4QsRXrQ=="],
+
+ "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-td5O8pFIgLs8H1sAZsD6v+5quODihyEw4nv2R8z7swUfIK1FKk+15e4eiYVLcAE4jUqngvh4j3JCNgg0Y4o4IQ=="],
+
+ "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+DZYv8l7FlUtTrWs1Tdt1KcNCAmRO87PyOnxKGunbWm5HKg1oZBSbIIPkjrCtDZaeqSG1DiGx7qF+CPsquQRcg=="],
+
+ "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-PYWgEO7up7XYwSAArOpzsVCiqxBCXy53gsReAb1kKYIyXaoAlhBaBMvxR/k2Rm9aTuZ662locXUmPk/Aj+Xu+Q=="],
+
+ "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Y3Ob4nqgv38Mh+6EGHltuN+Cq8aj/gyMTJYzkFZV2AEj+9XzoXB9VNljz9pjfFNHUxvLEV4b55VWyxozQTBaUQ=="],
+
+ "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RHIG/zgo+69idUqVvV3n8+j58dKYABRpMyDmfWu2TITC+jwGPiEaT0Q3RKD+kQHiS80mpBrST0iUGeEXT0bU9A=="],
+
+ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.1", "", { "os": "win32", "cpu": "x64" }, "sha512-izl30JJ5Dp10mi90Eko47zhxE6pYyWPcnX1NQxKpL/yMhXxf95oLTzfpu4q+MDBh/gemNqyJEwjBpe0MT5iWPA=="],
+
+ "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
+
+ "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
+
+ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.76.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@typescript-eslint/types": "^8.46.0", "comment-parser": "1.4.1", "esquery": "^1.6.0", "jsdoc-type-pratt-parser": "~6.10.0" } }, "sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w=="],
+
+ "@es-joy/resolve.exports": ["@es-joy/resolve.exports@1.0.0", "", {}, "sha512-bbrmzsAZ9GA/3oBS6r8PWMtZarEhKHr413hak8ArwMEZ5DtaLErnkcyEWUsXy7urBcmVu/TpDzHPDVM5uIbx9A=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
+
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
+
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
+
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
+
+ "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
+
+ "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
+
+ "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
+
+ "@eslint/config-helpers": ["@eslint/config-helpers@0.4.1", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw=="],
+
+ "@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="],
+
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+
+ "@eslint/js": ["@eslint/js@9.38.0", "", {}, "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A=="],
+
+ "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
+
+ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0", "levn": "^0.4.1" } }, "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A=="],
+
+ "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
+
+ "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
+
+ "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
+
+ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
+
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="],
+
+ "@kirick/lint": ["@kirick/lint@0.2.12", "", { "dependencies": { "@stylistic/eslint-plugin": "5.5.0", "eslint": "9.38.0", "eslint-plugin-jsdoc": "61.1.9", "eslint-plugin-n": "17.23.1", "eslint-plugin-promise": "7.2.1", "eslint-plugin-unicorn": "62.0.0", "eslint-plugin-vue": "10.5.1", "typescript-eslint": "8.46.2" }, "bin": { "lint": "dist/main.js" } }, "sha512-eE//5Z21/Sdb8JjhL2x27ZPDjaVLWmaIJ50zUJC0d08u6VPqCLPMVryYDM3NLJn8G9YrBLqmKDQ9X+0+dsuOWw=="],
+
+ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="],
+
+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+
+ "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+
+ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+
+ "@oxc-project/types": ["@oxc-project/types@0.95.0", "", {}, "sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ=="],
+
+ "@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.24.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1Kd2+Ai1ttskhbJR+DNU4Y4YEDyP/cd50nWt2rAe2aE78dMOalaVGps3s8UnJkXpDL9ZqkgOHVDE5Doj2lxatw=="],
+
+ "@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.24.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-/R9VbnuTp7bLIBh6ucDHjx0po0wLQODLqzy+L/Frn5z4ifMVdE63DB+LHO8QAj+WEQleQq3u/MMms7RFPulCLA=="],
+
+ "@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@1.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-fA90bIQ1b44eNg0uULlTonqsADVIBnMz169mav6IhfZL9V6DpBCUWrV+8tEQCxbDvYC0WY1guBpPo2QWUnC/Dw=="],
+
+ "@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@1.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-p7Bv9FTQ1lf4Z7OiIFwiy+cY2fxN6IJc0+2gJ4z2fpaQ0J2rQQcKdJ5RLQTxf+tAu7hyqjc6bf61EAGa9lb/GA=="],
+
+ "@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@1.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-wIQOpTONiJ9pYPnLEq7UFuml8mpmSFTfUveNbT2rw9iXfj2nLMf7NIqGnUYQdvnnOi+maag9uei/WImXIm9LQQ=="],
+
+ "@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@1.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-HxcDX/SpTH7yC/Rn2MinjSHZmNpn79yJkBid792DWjP9bo0CnlNXOXMPXsbm+WqptvqQ9yUPCxf7KascUvxLyQ=="],
+
+ "@oxlint/win32-arm64": ["@oxlint/win32-arm64@1.24.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-P1KtZ/xL+TcNTTmOtEsVrpqAdmpu2UCRAILjoqQyrYvI/CW6SdvoJfMBTntKOZaB52Peq2BHTgsYovON8q4FfQ=="],
+
+ "@oxlint/win32-x64": ["@oxlint/win32-x64@1.24.0", "", { "os": "win32", "cpu": "x64" }, "sha512-JMbMm7i1esFl12fRdOQwoeEeufWXxihOme8pZpI6jrwWK1kCIANMb5agI5Lkjf5vToQOP3DLXYc29aDm16fw6g=="],
+
+ "@publint/pack": ["@publint/pack@0.1.2", "", {}, "sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw=="],
+
+ "@quansync/fs": ["@quansync/fs@0.1.5", "", { "dependencies": { "quansync": "^0.2.11" } }, "sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA=="],
+
+ "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.45", "", { "os": "android", "cpu": "arm64" }, "sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g=="],
+
+ "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.45", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA=="],
+
+ "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.45", "", { "os": "darwin", "cpu": "x64" }, "sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw=="],
+
+ "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.45", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg=="],
+
+ "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45", "", { "os": "linux", "cpu": "arm" }, "sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog=="],
+
+ "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45", "", { "os": "linux", "cpu": "arm64" }, "sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ=="],
+
+ "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.45", "", { "os": "linux", "cpu": "arm64" }, "sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA=="],
+
+ "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.45", "", { "os": "linux", "cpu": "x64" }, "sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g=="],
+
+ "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.45", "", { "os": "linux", "cpu": "x64" }, "sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA=="],
+
+ "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.45", "", { "os": "none", "cpu": "arm64" }, "sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ=="],
+
+ "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.45", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.7" }, "cpu": "none" }, "sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ=="],
+
+ "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45", "", { "os": "win32", "cpu": "arm64" }, "sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ=="],
+
+ "@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45", "", { "os": "win32", "cpu": "ia32" }, "sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA=="],
+
+ "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.45", "", { "os": "win32", "cpu": "x64" }, "sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA=="],
+
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.45", "", {}, "sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
+
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
+
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
+
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
+
+ "@sindresorhus/base62": ["@sindresorhus/base62@1.0.0", "", {}, "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA=="],
+
+ "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
+
+ "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.5.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.0", "@typescript-eslint/types": "^8.46.1", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw=="],
+
+ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
+
+ "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
+
+ "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
+
+ "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
+
+ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
+
+ "@types/node": ["@types/node@20.16.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA=="],
+
+ "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
+
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/type-utils": "8.46.2", "@typescript-eslint/utils": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.2", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w=="],
+
+ "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g=="],
+
+ "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.2", "@typescript-eslint/types": "^8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg=="],
+
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2" } }, "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA=="],
+
+ "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag=="],
+
+ "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA=="],
+
+ "@typescript-eslint/types": ["@typescript-eslint/types@8.46.2", "", {}, "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ=="],
+
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.2", "@typescript-eslint/tsconfig-utils": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ=="],
+
+ "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg=="],
+
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w=="],
+
+ "@vitest/expect": ["@vitest/expect@4.0.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.4", "@vitest/utils": "4.0.4", "chai": "^6.0.1", "tinyrainbow": "^3.0.3" } }, "sha512-0ioMscWJtfpyH7+P82sGpAi3Si30OVV73jD+tEqXm5+rIx9LgnfdaOn45uaFkKOncABi/PHL00Yn0oW/wK4cXw=="],
+
+ "@vitest/mocker": ["@vitest/mocker@4.0.4", "", { "dependencies": { "@vitest/spy": "4.0.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.19" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-UTtKgpjWj+pvn3lUM55nSg34098obGhSHH+KlJcXesky8b5wCUgg7s60epxrS6yAG8slZ9W8T9jGWg4PisMf5Q=="],
+
+ "@vitest/pretty-format": ["@vitest/pretty-format@4.0.4", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-lHI2rbyrLVSd1TiHGJYyEtbOBo2SDndIsN3qY4o4xe2pBxoJLD6IICghNCvD7P+BFin6jeyHXiUICXqgl6vEaQ=="],
+
+ "@vitest/runner": ["@vitest/runner@4.0.4", "", { "dependencies": { "@vitest/utils": "4.0.4", "pathe": "^2.0.3" } }, "sha512-99EDqiCkncCmvIZj3qJXBZbyoQ35ghOwVWNnQ5nj0Hnsv4Qm40HmrMJrceewjLVvsxV/JSU4qyx2CGcfMBmXJw=="],
+
+ "@vitest/snapshot": ["@vitest/snapshot@4.0.4", "", { "dependencies": { "@vitest/pretty-format": "4.0.4", "magic-string": "^0.30.19", "pathe": "^2.0.3" } }, "sha512-XICqf5Gi4648FGoBIeRgnHWSNDp+7R5tpclGosFaUUFzY6SfcpsfHNMnC7oDu/iOLBxYfxVzaQpylEvpgii3zw=="],
+
+ "@vitest/spy": ["@vitest/spy@4.0.4", "", {}, "sha512-G9L13AFyYECo40QG7E07EdYnZZYCKMTSp83p9W8Vwed0IyCG1GnpDLxObkx8uOGPXfDpdeVf24P1Yka8/q1s9g=="],
+
+ "@vitest/utils": ["@vitest/utils@4.0.4", "", { "dependencies": { "@vitest/pretty-format": "4.0.4", "tinyrainbow": "^3.0.3" } }, "sha512-4bJLmSvZLyVbNsYFRpPYdJViG9jZyRvMZ35IF4ymXbRZoS+ycYghmwTGiscTXduUg2lgKK7POWIyXJNute1hjw=="],
+
+ "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
+
+ "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="],
+
+ "are-docs-informative": ["are-docs-informative@0.0.2", "", {}, "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig=="],
+
+ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+ "ast-kit": ["ast-kit@2.1.3", "", { "dependencies": { "@babel/parser": "^7.28.4", "pathe": "^2.0.3" } }, "sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.8.20", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ=="],
+
+ "birpc": ["birpc@2.6.1", "", {}, "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ=="],
+
+ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
+
+ "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
+
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
+ "browserslist": ["browserslist@4.27.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", "electron-to-chromium": "^1.5.238", "node-releases": "^2.0.26", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw=="],
+
+ "builtin-modules": ["builtin-modules@5.0.0", "", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="],
+
+ "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
+
+ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
+
+ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="],
+
+ "chai": ["chai@6.2.0", "", {}, "sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA=="],
+
+ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+
+ "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="],
+
+ "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
+
+ "ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
+
+ "clean-regexp": ["clean-regexp@1.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "comment-parser": ["comment-parser@1.4.1", "", {}, "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg=="],
+
+ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+
+ "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
+
+ "core-js-compat": ["core-js-compat@3.46.0", "", { "dependencies": { "browserslist": "^4.26.3" } }, "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law=="],
+
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+ "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
+
+ "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
+
+ "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
+
+ "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
+
+ "dts-resolver": ["dts-resolver@2.1.2", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg=="],
+
+ "electron-to-chromium": ["electron-to-chromium@1.5.243", "", {}, "sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g=="],
+
+ "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.17.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg=="],
+
+ "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
+
+ "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
+
+ "eslint": ["eslint@9.38.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.1", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.38.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw=="],
+
+ "eslint-compat-utils": ["eslint-compat-utils@0.5.1", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q=="],
+
+ "eslint-plugin-es-x": ["eslint-plugin-es-x@7.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", "eslint-compat-utils": "^0.5.1" }, "peerDependencies": { "eslint": ">=8" } }, "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ=="],
+
+ "eslint-plugin-jsdoc": ["eslint-plugin-jsdoc@61.1.9", "", { "dependencies": { "@es-joy/jsdoccomment": "~0.76.0", "@es-joy/resolve.exports": "1.0.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", "espree": "^10.4.0", "esquery": "^1.6.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", "semver": "^7.7.3", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-X2AzSGbq1CzBRgKcVAu2qzOV9ogqygkUDk5AX6eNK5G+kY3I5Op5E5b99fE+FN0/bGnk2KGcsMIG6ZLF+di69A=="],
+
+ "eslint-plugin-n": ["eslint-plugin-n@17.23.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", "enhanced-resolve": "^5.17.1", "eslint-plugin-es-x": "^7.8.0", "get-tsconfig": "^4.8.1", "globals": "^15.11.0", "globrex": "^0.1.2", "ignore": "^5.3.2", "semver": "^7.6.3", "ts-declaration-location": "^1.0.6" }, "peerDependencies": { "eslint": ">=8.23.0" } }, "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A=="],
+
+ "eslint-plugin-promise": ["eslint-plugin-promise@7.2.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA=="],
+
+ "eslint-plugin-unicorn": ["eslint-plugin-unicorn@62.0.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "@eslint-community/eslint-utils": "^4.9.0", "@eslint/plugin-kit": "^0.4.0", "change-case": "^5.4.4", "ci-info": "^4.3.1", "clean-regexp": "^1.0.0", "core-js-compat": "^3.46.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", "globals": "^16.4.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.13.0", "semver": "^7.7.3", "strip-indent": "^4.1.1" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g=="],
+
+ "eslint-plugin-vue": ["eslint-plugin-vue@10.5.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" }, "optionalPeers": ["@stylistic/eslint-plugin", "@typescript-eslint/parser"] }, "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ=="],
+
+ "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
+
+ "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
+
+ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
+
+ "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
+
+ "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
+
+ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
+
+ "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
+
+ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
+
+ "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
+
+ "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
+
+ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
+
+ "fast-glob": ["fast-glob@3.3.2", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="],
+
+ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
+
+ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
+
+ "fastq": ["fastq@1.17.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w=="],
+
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
+ "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
+ "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
+
+ "find-up-simple": ["find-up-simple@1.0.1", "", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="],
+
+ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
+
+ "flatted": ["flatted@3.3.1", "", {}, "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
+
+ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
+
+ "globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
+
+ "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
+
+ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+
+ "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
+
+ "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
+
+ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
+
+ "import-fresh": ["import-fresh@3.3.0", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="],
+
+ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+
+ "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
+
+ "is-builtin-module": ["is-builtin-module@5.0.0", "", { "dependencies": { "builtin-modules": "^5.0.0" } }, "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA=="],
+
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+ "itty-router": ["itty-router@5.0.22", "", {}, "sha512-9hmdGErWdYDOurGYxSbqLhy4EFReIwk71hMZTJ5b+zfa2zjMNV1ftFno2b8VjAQvX615gNB8Qxbl9JMRqHnIVA=="],
+
+ "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
+
+ "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
+
+ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+
+ "jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@6.10.0", "", {}, "sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ=="],
+
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
+ "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
+
+ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
+
+ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
+
+ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
+
+ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
+
+ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
+
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
+
+ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
+ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
+
+ "neoevents": ["neoevents@0.3.0", "", {}, "sha512-et9vcJ8ElRUbgxeJXAw++1noXVY302fxRvwEoIDll9KiQAeySjTQBdLtATvtE17MgbitLjjixI5UcFOS92EI+w=="],
+
+ "node-releases": ["node-releases@2.0.26", "", {}, "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA=="],
+
+ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
+
+ "object-deep-merge": ["object-deep-merge@2.0.0", "", {}, "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg=="],
+
+ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
+
+ "oxlint": ["oxlint@1.24.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.24.0", "@oxlint/darwin-x64": "1.24.0", "@oxlint/linux-arm64-gnu": "1.24.0", "@oxlint/linux-arm64-musl": "1.24.0", "@oxlint/linux-x64-gnu": "1.24.0", "@oxlint/linux-x64-musl": "1.24.0", "@oxlint/win32-arm64": "1.24.0", "@oxlint/win32-x64": "1.24.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.2.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint", "oxc_language_server": "bin/oxc_language_server" } }, "sha512-swXlnHT7ywcCApkctIbgOSjDYHwMa12yMU0iXevfDuHlYkRUcbQrUv6nhM5v6B0+Be3zTBMNDGPAMQv0oznzRQ=="],
+
+ "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
+
+ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
+
+ "package-manager-detector": ["package-manager-detector@1.3.0", "", {}, "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="],
+
+ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
+
+ "parse-imports-exports": ["parse-imports-exports@0.2.4", "", { "dependencies": { "parse-statements": "1.0.11" } }, "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ=="],
+
+ "parse-statements": ["parse-statements@1.0.11", "", {}, "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA=="],
+
+ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
+
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
+
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+
+ "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
+
+ "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
+
+ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
+
+ "publint": ["publint@0.3.15", "", { "dependencies": { "@publint/pack": "^0.1.2", "package-manager-detector": "^1.3.0", "picocolors": "^1.1.1", "sade": "^1.8.1" }, "bin": { "publint": "src/cli.js" } }, "sha512-xPbRAPW+vqdiaKy5sVVY0uFAu3LaviaPO3pZ9FaRx59l9+U/RKR1OEbLhkug87cwiVKxPXyB4txsv5cad67u+A=="],
+
+ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+
+ "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
+
+ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+
+ "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
+
+ "regexp-tree": ["regexp-tree@0.1.27", "", { "bin": { "regexp-tree": "bin/regexp-tree" } }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="],
+
+ "regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
+
+ "reserved-identifiers": ["reserved-identifiers@1.2.0", "", {}, "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw=="],
+
+ "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
+
+ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
+
+ "reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="],
+
+ "rolldown": ["rolldown@1.0.0-beta.45", "", { "dependencies": { "@oxc-project/types": "=0.95.0", "@rolldown/pluginutils": "1.0.0-beta.45" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.45", "@rolldown/binding-darwin-arm64": "1.0.0-beta.45", "@rolldown/binding-darwin-x64": "1.0.0-beta.45", "@rolldown/binding-freebsd-x64": "1.0.0-beta.45", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.45", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.45", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.45", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.45", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.45", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.45", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.45", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.45", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.45", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.45" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ=="],
+
+ "rolldown-plugin-dts": ["rolldown-plugin-dts@0.17.3", "", { "dependencies": { "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ast-kit": "^2.1.3", "birpc": "^2.6.1", "debug": "^4.4.3", "dts-resolver": "^2.1.2", "get-tsconfig": "^4.13.0", "magic-string": "^0.30.21" }, "peerDependencies": { "@ts-macro/tsc": "^0.3.6", "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.44", "typescript": "^5.0.0", "vue-tsc": "~3.1.0" }, "optionalPeers": ["@ts-macro/tsc", "@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-8mGnNUVNrqEdTnrlcaDxs4sAZg0No6njO+FuhQd4L56nUbJO1tHxOoKDH3mmMJg7f/BhEj/1KjU5W9kZ9zM/kQ=="],
+
+ "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
+
+ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+
+ "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
+
+ "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
+
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
+
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+
+ "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
+
+ "spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="],
+
+ "spdx-license-ids": ["spdx-license-ids@3.0.20", "", {}, "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw=="],
+
+ "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
+
+ "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
+
+ "strip-indent": ["strip-indent@4.1.1", "", {}, "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA=="],
+
+ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
+
+ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+
+ "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
+
+ "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
+
+ "tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
+
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
+
+ "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
+ "to-valid-identifier": ["to-valid-identifier@1.0.0", "", { "dependencies": { "@sindresorhus/base62": "^1.0.0", "reserved-identifiers": "^1.0.0" } }, "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw=="],
+
+ "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
+
+ "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
+
+ "ts-declaration-location": ["ts-declaration-location@1.0.7", "", { "dependencies": { "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": ">=4.0.0" } }, "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA=="],
+
+ "tsdown": ["tsdown@0.15.12", "", { "dependencies": { "ansis": "^4.2.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.3", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "1.0.0-beta.45", "rolldown-plugin-dts": "^0.17.2", "semver": "^7.7.3", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.15", "tree-kill": "^1.2.2", "unconfig": "^7.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0", "unrun": "^0.2.1" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused", "unrun"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-c8VLlQm8/lFrOAg5VMVeN4NAbejZyVQkzd+ErjuaQgJFI/9MhR9ivr0H/CM7UlOF1+ELlF6YaI7sU/4itgGQ8w=="],
+
+ "tslib": ["tslib@2.7.0", "", {}, "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="],
+
+ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
+
+ "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "typescript-eslint": ["typescript-eslint@8.46.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.2", "@typescript-eslint/parser": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg=="],
+
+ "unconfig": ["unconfig@7.3.3", "", { "dependencies": { "@quansync/fs": "^0.1.5", "defu": "^6.1.4", "jiti": "^2.5.1", "quansync": "^0.2.11" } }, "sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA=="],
+
+ "undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
+
+ "unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="],
+
+ "unplugin-unused": ["unplugin-unused@0.5.4", "", { "dependencies": { "js-tokens": "^9.0.1", "pkg-types": "^2.3.0", "unplugin": "^2.3.10" } }, "sha512-R11StgC5j43CECdjHFbc6Ep4MgM97xuI7rku/nCw7OpIEw9sG4btxvR7Ld4RViwAF+eEQjSebesE+jTJTFFWmQ=="],
+
+ "update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+
+ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+
+ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
+
+ "valibot": ["valibot@1.1.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw=="],
+
+ "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="],
+
+ "vitest": ["vitest@4.0.4", "", { "dependencies": { "@vitest/expect": "4.0.4", "@vitest/mocker": "4.0.4", "@vitest/pretty-format": "4.0.4", "@vitest/runner": "4.0.4", "@vitest/snapshot": "4.0.4", "@vitest/spy": "4.0.4", "@vitest/utils": "4.0.4", "debug": "^4.4.3", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.19", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.4", "@vitest/browser-preview": "4.0.4", "@vitest/browser-webdriverio": "4.0.4", "@vitest/ui": "4.0.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hV31h0/bGbtmDQc0KqaxsTO1v4ZQeF8ojDFuy4sZhFadwAqqvJA0LDw68QUocctI5EDpFMql/jVWKuPYHIf2Ew=="],
+
+ "vue-eslint-parser": ["vue-eslint-parser@9.4.3", "", { "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", "eslint-visitor-keys": "^3.3.0", "espree": "^9.3.1", "esquery": "^1.4.0", "lodash": "^4.17.21", "semver": "^7.3.6" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg=="],
+
+ "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
+
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+ "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
+
+ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
+
+ "xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="],
+
+ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+
+ "@es-joy/jsdoccomment/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "@eslint/config-array/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "@eslint/eslintrc/espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
+
+ "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
+
+ "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.0", "", {}, "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew=="],
+
+ "@jridgewell/gen-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+
+ "@jridgewell/remapping/@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
+
+ "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
+
+ "@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+
+ "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.4", "", {}, "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A=="],
+
+ "@typescript-eslint/parser/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "@typescript-eslint/project-service/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "@typescript-eslint/type-utils/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "@typescript-eslint/typescript-estree/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
+
+ "eslint-compat-utils/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
+
+ "eslint-plugin-es-x/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.0", "", { "dependencies": { "eslint-visitor-keys": "^3.3.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA=="],
+
+ "eslint-plugin-es-x/@eslint-community/regexpp": ["@eslint-community/regexpp@4.11.0", "", {}, "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A=="],
+
+ "eslint-plugin-jsdoc/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "eslint-plugin-promise/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.0", "", { "dependencies": { "eslint-visitor-keys": "^3.3.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA=="],
+
+ "eslint-plugin-unicorn/globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
+
+ "estree-walker/@types/estree": ["@types/estree@1.0.5", "", {}, "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="],
+
+ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "rolldown-plugin-dts/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "rolldown-plugin-dts/get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
+
+ "rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "tsdown/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "unconfig/jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
+
+ "vitest/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
+
+ "vue-eslint-parser/debug": ["debug@4.3.6", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg=="],
+
+ "vue-eslint-parser/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="],
+
+ "vue-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "vue-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
+
+ "vue-eslint-parser/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
+
+ "@eslint/eslintrc/espree/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
+
+ "@eslint/eslintrc/espree/eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
+
+ "@jridgewell/remapping/@jridgewell/gen-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+
+ "@jridgewell/remapping/@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+
+ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
+
+ "eslint-plugin-es-x/@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "eslint-plugin-promise/@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "vue-eslint-parser/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
+
+ "vue-eslint-parser/espree/acorn": ["acorn@8.12.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="],
+ }
+}
diff --git a/dist/dev.cjs b/dist/dev.cjs
new file mode 100644
index 0000000..208eaac
--- /dev/null
+++ b/dist/dev.cjs
@@ -0,0 +1,31 @@
+const require_record = require('./record-D97pjgdq.cjs');
+let neoevents = require("neoevents");
+neoevents = require_record.__toESM(neoevents);
+
+//#region src/driver.ts
+var HyperAPIDriver = class extends neoevents.NeoEventTarget {
+ emitRequest(request) {
+ return new Promise((resolve) => {
+ this.emit("request", {
+ request,
+ callback: resolve
+ });
+ });
+ }
+};
+
+//#endregion
+//#region src/utils/methods.ts
+/**
+* Checks if the given value is a valid HyperAPI method.
+* @param method The HTTP method to check.
+* @returns -
+*/
+function isHyperApiMethod(method) {
+ return method === "DELETE" || method === "GET" || method === "HEAD" || method === "OPTIONS" || method === "PATCH" || method === "POST" || method === "PUT" || method === "UNKNOWN";
+}
+
+//#endregion
+exports.HyperAPIDriver = HyperAPIDriver;
+exports.isHyperApiMethod = isHyperApiMethod;
+exports.isRecord = require_record.isRecord;
\ No newline at end of file
diff --git a/dist/dev.d.cts b/dist/dev.d.cts
new file mode 100644
index 0000000..b4c30d2
--- /dev/null
+++ b/dist/dev.d.cts
@@ -0,0 +1,2 @@
+import { c as HyperAPIErrorData, d as BaseRecord, f as EmptyObject, h as isHyperApiMethod, i as HyperAPIModuleResponse, l as HyperAPIRequest, m as HyperAPIMethod, n as HyperAPIResponse, p as isRecord, r as HyperAPIModule, t as HyperAPIDriver, u as HyperAPIRequestArgs } from "./driver-DO8IdV_Q.cjs";
+export { type BaseRecord, type EmptyObject, HyperAPIDriver, type HyperAPIErrorData, type HyperAPIMethod, type HyperAPIModule, type HyperAPIModuleResponse, type HyperAPIRequest, type HyperAPIRequestArgs, type HyperAPIResponse, isHyperApiMethod, isRecord };
\ No newline at end of file
diff --git a/dist/dev.d.ts b/dist/dev.d.ts
new file mode 100644
index 0000000..429610f
--- /dev/null
+++ b/dist/dev.d.ts
@@ -0,0 +1,2 @@
+import { c as HyperAPIErrorData, d as BaseRecord, f as EmptyObject, h as isHyperApiMethod, i as HyperAPIModuleResponse, l as HyperAPIRequest, m as HyperAPIMethod, n as HyperAPIResponse, p as isRecord, r as HyperAPIModule, t as HyperAPIDriver, u as HyperAPIRequestArgs } from "./driver-D8sen_tB.js";
+export { type BaseRecord, type EmptyObject, HyperAPIDriver, type HyperAPIErrorData, type HyperAPIMethod, type HyperAPIModule, type HyperAPIModuleResponse, type HyperAPIRequest, type HyperAPIRequestArgs, type HyperAPIResponse, isHyperApiMethod, isRecord };
\ No newline at end of file
diff --git a/dist/dev.js b/dist/dev.js
new file mode 100644
index 0000000..5e97483
--- /dev/null
+++ b/dist/dev.js
@@ -0,0 +1,28 @@
+import { n as isRecord } from "./record-BeJ2ZKAR.js";
+import { NeoEvent, NeoEventTarget } from "neoevents";
+
+//#region src/driver.ts
+var HyperAPIDriver = class extends NeoEventTarget {
+ emitRequest(request) {
+ return new Promise((resolve) => {
+ this.emit("request", {
+ request,
+ callback: resolve
+ });
+ });
+ }
+};
+
+//#endregion
+//#region src/utils/methods.ts
+/**
+* Checks if the given value is a valid HyperAPI method.
+* @param method The HTTP method to check.
+* @returns -
+*/
+function isHyperApiMethod(method) {
+ return method === "DELETE" || method === "GET" || method === "HEAD" || method === "OPTIONS" || method === "PATCH" || method === "POST" || method === "PUT" || method === "UNKNOWN";
+}
+
+//#endregion
+export { HyperAPIDriver, isHyperApiMethod, isRecord };
\ No newline at end of file
diff --git a/dist/driver-D8sen_tB.d.ts b/dist/driver-D8sen_tB.d.ts
new file mode 100644
index 0000000..dbedd29
--- /dev/null
+++ b/dist/driver-D8sen_tB.d.ts
@@ -0,0 +1,91 @@
+import { NeoEvent, NeoEventTarget } from "neoevents";
+import { IsEqual, IsNever, Merge, Promisable } from "type-fest";
+
+//#region src/utils/methods.d.ts
+type HyperAPIMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "UNKNOWN";
+/**
+* Checks if the given value is a valid HyperAPI method.
+* @param method The HTTP method to check.
+* @returns -
+*/
+declare function isHyperApiMethod(method: unknown): method is HyperAPIMethod;
+//#endregion
+//#region src/utils/record.d.ts
+type BaseRecord = Record;
+type EmptyObject = Record;
+/**
+* Check if a value is a record.
+* @param value -
+* @returns -
+*/
+declare function isRecord(value: unknown): value is BaseRecord;
+//#endregion
+//#region src/request.d.ts
+type HyperAPIRequestArgs = BaseRecord;
+interface HyperAPIRequest {
+ method: HyperAPIMethod;
+ path: string;
+ args: A;
+}
+//#endregion
+//#region src/error.d.ts
+type HyperAPIErrorData = Record | undefined;
+interface HyperAPIErrorResponse {
+ code: number;
+ description?: string;
+ data?: HyperAPIErrorData;
+}
+declare class HyperAPIError extends Error {
+ /** The error code. */
+ readonly code: number;
+ /** The error description. */
+ readonly description: string;
+ /** The error data. */
+ readonly data?: D;
+ /** HTTP status code. */
+ readonly httpStatus?: number;
+ /** HTTP headers to return. */
+ readonly httpHeaders?: Record;
+ constructor(data?: D);
+ get message(): string;
+ /**
+ * Creates response object.
+ * @returns -
+ */
+ getResponse(): HyperAPIErrorResponse;
+}
+//#endregion
+//#region src/utils/types.d.ts
+type IsRecord = IsNever extends true ? false : T extends void ? false : IsEqual extends true ? false : true;
+type Join, ReqExtra extends BaseRecord> = IsRecord extends true ? Merge : R;
+type Extend = IsRecord extends true ? IsRecord extends true ? Merge : V1 : IsRecord extends true ? Exclude : EmptyObject;
+//#endregion
+//#region src/module.d.ts
+type HyperAPIModuleResponse = Response | BaseRecord | unknown[] | undefined;
+declare class HyperAPIModule, ReqExtra extends BaseRecord = never> {
+ private chain;
+ use(fn: (request: Join) => Promisable): HyperAPIModule>;
+ set(key: K, fn: (request: Join) => Promisable): HyperAPIModule>;
+ set(key: K, value: V): HyperAPIModule>;
+ action(fn: (request: Join) => Promisable): HyperAPIModule>;
+ _run(request: Req): Promise>>;
+}
+//#endregion
+//#region src/response.d.ts
+type HyperAPIResponse = HyperAPIModuleResponse | HyperAPIError;
+//#endregion
+//#region src/driver.d.ts
+declare class HyperAPIDriver extends NeoEventTarget<{
+ request: NeoEvent<{
+ request: R;
+ callback: (response: HyperAPIResponse) => void;
+ }>;
+}> {
+ protected emitRequest(request: R): Promise;
+}
+//#endregion
+export { Extend as a, HyperAPIErrorData as c, BaseRecord as d, EmptyObject as f, isHyperApiMethod as h, HyperAPIModuleResponse as i, HyperAPIRequest as l, HyperAPIMethod as m, HyperAPIResponse as n, Join as o, isRecord as p, HyperAPIModule as r, HyperAPIError as s, HyperAPIDriver as t, HyperAPIRequestArgs as u };
\ No newline at end of file
diff --git a/dist/driver-DO8IdV_Q.d.cts b/dist/driver-DO8IdV_Q.d.cts
new file mode 100644
index 0000000..dbedd29
--- /dev/null
+++ b/dist/driver-DO8IdV_Q.d.cts
@@ -0,0 +1,91 @@
+import { NeoEvent, NeoEventTarget } from "neoevents";
+import { IsEqual, IsNever, Merge, Promisable } from "type-fest";
+
+//#region src/utils/methods.d.ts
+type HyperAPIMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "UNKNOWN";
+/**
+* Checks if the given value is a valid HyperAPI method.
+* @param method The HTTP method to check.
+* @returns -
+*/
+declare function isHyperApiMethod(method: unknown): method is HyperAPIMethod;
+//#endregion
+//#region src/utils/record.d.ts
+type BaseRecord = Record;
+type EmptyObject = Record;
+/**
+* Check if a value is a record.
+* @param value -
+* @returns -
+*/
+declare function isRecord(value: unknown): value is BaseRecord;
+//#endregion
+//#region src/request.d.ts
+type HyperAPIRequestArgs = BaseRecord;
+interface HyperAPIRequest {
+ method: HyperAPIMethod;
+ path: string;
+ args: A;
+}
+//#endregion
+//#region src/error.d.ts
+type HyperAPIErrorData = Record | undefined;
+interface HyperAPIErrorResponse {
+ code: number;
+ description?: string;
+ data?: HyperAPIErrorData;
+}
+declare class HyperAPIError extends Error {
+ /** The error code. */
+ readonly code: number;
+ /** The error description. */
+ readonly description: string;
+ /** The error data. */
+ readonly data?: D;
+ /** HTTP status code. */
+ readonly httpStatus?: number;
+ /** HTTP headers to return. */
+ readonly httpHeaders?: Record;
+ constructor(data?: D);
+ get message(): string;
+ /**
+ * Creates response object.
+ * @returns -
+ */
+ getResponse(): HyperAPIErrorResponse;
+}
+//#endregion
+//#region src/utils/types.d.ts
+type IsRecord = IsNever extends true ? false : T extends void ? false : IsEqual extends true ? false : true;
+type Join, ReqExtra extends BaseRecord> = IsRecord extends true ? Merge : R;
+type Extend = IsRecord extends true ? IsRecord extends true ? Merge : V1 : IsRecord extends true ? Exclude : EmptyObject;
+//#endregion
+//#region src/module.d.ts
+type HyperAPIModuleResponse = Response | BaseRecord | unknown[] | undefined;
+declare class HyperAPIModule, ReqExtra extends BaseRecord = never> {
+ private chain;
+ use(fn: (request: Join) => Promisable): HyperAPIModule>;
+ set(key: K, fn: (request: Join) => Promisable): HyperAPIModule>;
+ set(key: K, value: V): HyperAPIModule>;
+ action(fn: (request: Join) => Promisable): HyperAPIModule>;
+ _run(request: Req): Promise>>;
+}
+//#endregion
+//#region src/response.d.ts
+type HyperAPIResponse = HyperAPIModuleResponse | HyperAPIError;
+//#endregion
+//#region src/driver.d.ts
+declare class HyperAPIDriver extends NeoEventTarget<{
+ request: NeoEvent<{
+ request: R;
+ callback: (response: HyperAPIResponse) => void;
+ }>;
+}> {
+ protected emitRequest(request: R): Promise;
+}
+//#endregion
+export { Extend as a, HyperAPIErrorData as c, BaseRecord as d, EmptyObject as f, isHyperApiMethod as h, HyperAPIModuleResponse as i, HyperAPIRequest as l, HyperAPIMethod as m, HyperAPIResponse as n, Join as o, isRecord as p, HyperAPIModule as r, HyperAPIError as s, HyperAPIDriver as t, HyperAPIRequestArgs as u };
\ No newline at end of file
diff --git a/dist/main.cjs b/dist/main.cjs
new file mode 100644
index 0000000..a984f5c
--- /dev/null
+++ b/dist/main.cjs
@@ -0,0 +1,457 @@
+const require_record = require('./record-D97pjgdq.cjs');
+let node_path = require("node:path");
+node_path = require_record.__toESM(node_path);
+let itty_router = require("itty-router");
+itty_router = require_record.__toESM(itty_router);
+let node_fs = require("node:fs");
+node_fs = require_record.__toESM(node_fs);
+
+//#region src/error.ts
+var HyperAPIError = class extends Error {
+ /** The error code. */
+ code = 0;
+ /** The error description. */
+ description = "HyperAPI error";
+ /** The error data. */
+ data;
+ /** HTTP status code. */
+ httpStatus;
+ /** HTTP headers to return. */
+ httpHeaders;
+ constructor(data) {
+ super();
+ if (require_record.isRecord(data)) this.data = data;
+ }
+ get message() {
+ return `${this.description} (code ${this.code}).`;
+ }
+ /**
+ * Creates response object.
+ * @returns -
+ */
+ getResponse() {
+ const response = { code: this.code };
+ if (typeof this.description === "string") response.description = this.description;
+ if (this.data) response.data = this.data;
+ return response;
+ }
+};
+
+//#endregion
+//#region src/api-errors.ts
+var HyperAPIAuthorizationError = class extends HyperAPIError {
+ code = 1;
+ description = "Authorization error";
+ httpStatus = 401;
+};
+var HyperAPIInvalidParametersError = class extends HyperAPIError {
+ code = 2;
+ description = "One of the parameters specified was missing or invalid";
+ httpStatus = 400;
+};
+var HyperAPIInternalError = class extends HyperAPIError {
+ code = 3;
+ description = "Internal error";
+ httpStatus = 500;
+};
+var HyperAPIForbiddenError = class extends HyperAPIError {
+ code = 4;
+ description = "You do not have permission to perform this action";
+ httpStatus = 403;
+};
+var HyperAPIUnknownMethodError = class extends HyperAPIError {
+ code = 5;
+ description = "Unknown method called";
+ httpStatus = 404;
+};
+var HyperAPIUnknownMethodNotAllowedError = class extends HyperAPIError {
+ code = 5;
+ description = "Unknown method called";
+ httpStatus = 405;
+};
+var HyperAPIObjectsLimitError = class extends HyperAPIError {
+ code = 6;
+ description = "Too many objects requested";
+ httpStatus = 400;
+};
+var HyperAPIRateLimitError = class extends HyperAPIError {
+ code = 7;
+ description = "Rate limit exceeded";
+ httpStatus = 429;
+};
+var HyperAPICaptchaError = class extends HyperAPIError {
+ code = 8;
+ description = "Captcha required";
+ httpStatus = 428;
+};
+var HyperAPIBusyError = class extends HyperAPIError {
+ code = 10;
+ description = "Endpoint is busy";
+ httpStatus = 503;
+};
+var HyperAPIConfirmationError = class extends HyperAPIError {
+ code = 11;
+ description = "Confirmation required";
+ httpStatus = 409;
+};
+var HyperAPIOTPError = class extends HyperAPIError {
+ code = 12;
+ description = "One-time password required";
+ httpStatus = 401;
+};
+var HyperAPIMaintenanceError = class extends HyperAPIError {
+ code = 13;
+ description = "Endpoint is in maintenance mode";
+ httpStatus = 503;
+};
+var HyperAPIMethodNotAllowedError = class extends HyperAPIError {
+ code = 14;
+ description = "HTTP method not allowed";
+ httpStatus = 405;
+};
+
+//#endregion
+//#region src/module.ts
+var HyperAPIModule = class {
+ chain = [];
+ use(fn) {
+ this.chain.push(fn);
+ return this;
+ }
+ set(key, arg1) {
+ this.chain.push(async (request) => {
+ const value = typeof arg1 === "function" ? await arg1(request) : arg1;
+ if (value) return { [key]: value };
+ });
+ return this;
+ }
+ action(fn) {
+ this.chain.push(async (request) => {
+ const response = await fn(request);
+ if (response) return { response };
+ });
+ return this;
+ }
+ async _run(request) {
+ const request_result = request;
+ for (const fn of this.chain) {
+ const request_add = await fn(request_result);
+ if (request_add) Object.assign(request_result, request_add);
+ }
+ return request_result;
+ }
+};
+
+//#endregion
+//#region src/response.ts
+/**
+* Checks if the given value is a HyperAPIResponse.
+* @param response - The value to check.
+* @returns True if the value is a HyperAPIResponse, false otherwise.
+*/
+function isHyperAPIResponse(response) {
+ return response instanceof HyperAPIError || response instanceof Response || require_record.isRecord(response) || Array.isArray(response) || response === void 0;
+}
+
+//#endregion
+//#region src/router/filename.ts
+const RE_OPTIONAL_CATCH_ALL = /^\[\[\.\.\.([a-z_][\da-z_]*)\]\]$/i;
+const RE_GREEDY = /^\[\.\.\.([a-z_][\da-z_]*)\]$/i;
+/**
+* Returns specificity for route.
+*
+* Values:
+* - 0: static route (e.g. `/foo`)
+* - 1: route with parameter (e.g. `/foo-:id`)
+* - 2: route with optional parameter (e.g. `/foo-:id?`)
+* - 3: route with greedy parameter (e.g. `/foo-:id+`)
+* - 4: (NOT USED) route with wildcard (e.g. `/*`)
+* @param name -
+* @returns -
+*/
+function parseFilename(name) {
+ if (name === "index" || name.length === 0) return { self: true };
+ const match_optional_catch_all = RE_OPTIONAL_CATCH_ALL.exec(name);
+ if (match_optional_catch_all) return {
+ self: true,
+ route: {
+ part: `:${match_optional_catch_all[1]}+`,
+ specificity: {
+ type: 3,
+ static_length: 0
+ }
+ }
+ };
+ const match_greedy = RE_GREEDY.exec(name);
+ if (match_greedy) return { route: {
+ part: `:${match_greedy[1]}+`,
+ specificity: {
+ type: 3,
+ static_length: 0
+ }
+ } };
+ let has_optional = false;
+ let static_length = name.length;
+ const route_part = name.replaceAll(/(\[([a-z_][\da-z_]*)\]|\[\[([a-z_][\da-z_]*)\]\])([^\da-z_]|$)/gi, (...args) => {
+ static_length -= args[1].length;
+ if (args[3] !== void 0) {
+ has_optional = true;
+ return `:${args[3]}?${args[4]}`;
+ }
+ return `:${args[2]}${args[4]}`;
+ });
+ if (name !== route_part) return { route: {
+ part: route_part,
+ specificity: {
+ type: has_optional ? 2 : 1,
+ static_length
+ }
+ } };
+ if (name.includes("[") !== true) return { route: {
+ part: route_part,
+ specificity: {
+ type: 0,
+ static_length: 0
+ }
+ } };
+ throw new Error(`Invalid filename "${name}".`);
+}
+
+//#endregion
+//#region src/router/file-tree.ts
+const RE_EXT = /\.([cm]?[jt]s)$/i;
+const RE_METHOD = /\.(delete|get|head|options|patch|post|put)$/i;
+var WalkSpecificityPosition = /* @__PURE__ */ function(WalkSpecificityPosition$1) {
+ WalkSpecificityPosition$1[WalkSpecificityPosition$1["FILE_METHOD"] = 0] = "FILE_METHOD";
+ WalkSpecificityPosition$1[WalkSpecificityPosition$1["FILE_ALL"] = 1] = "FILE_ALL";
+ WalkSpecificityPosition$1[WalkSpecificityPosition$1["DIRECTORY"] = 2] = "DIRECTORY";
+ return WalkSpecificityPosition$1;
+}(WalkSpecificityPosition || {});
+/**
+* Reads the file system and returns a tree of routes sorted correctly.
+* @param path_given The directory to read.
+* @param _state Internal state.
+* @returns -
+*/
+function readFiles(path_given, _state) {
+ _state ??= {
+ route: null,
+ specificity: {
+ type: 0,
+ static_length: 0,
+ position: WalkSpecificityPosition.DIRECTORY
+ }
+ };
+ const result_directory = {
+ specificity: _state.specificity,
+ children: []
+ };
+ const result_routes = [];
+ const entries = (0, node_fs.readdirSync)(path_given, { withFileTypes: true });
+ if (process.env.NODE_ENV === "test") for (let i = entries.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [entries[i], entries[j]] = [entries[j], entries[i]];
+ }
+ for (const entry of entries) {
+ const path = node_path.default.join(entry.parentPath, entry.name);
+ if (entry.isFile()) {
+ let name = entry.name.replace(RE_EXT, "");
+ if (name.endsWith(".test")) continue;
+ let method = "all";
+ name = name.replace(RE_METHOD, (_, g1) => {
+ method = g1.toLowerCase();
+ return "";
+ });
+ const { self, route } = parseFilename(name);
+ const specificity_position = method === "all" ? WalkSpecificityPosition.FILE_ALL : WalkSpecificityPosition.FILE_METHOD;
+ if (self) result_routes.push({
+ specificity: {
+ ..._state.specificity,
+ position: specificity_position
+ },
+ method,
+ route: _state.route ?? "/",
+ path
+ });
+ if (route) result_directory.children.push({
+ specificity: {
+ ...route.specificity,
+ position: specificity_position
+ },
+ method,
+ route: (_state.route ?? "") + node_path.default.sep + route.part,
+ path
+ });
+ } else if (entry.isDirectory()) {
+ const { self, route } = parseFilename(entry.name);
+ if (self) throw new Error(`Invalid directory name "${entry.name}" at "${path}". Can not use optional catch-all in directory name.`);
+ if (!route) throw new Error(`Invalid directory name "${entry.name}" at "${path}".`);
+ result_directory.children.push(...readFiles(path, {
+ route: (_state.route ?? "") + node_path.default.sep + route.part,
+ specificity: {
+ ...route.specificity,
+ position: WalkSpecificityPosition.DIRECTORY
+ }
+ }));
+ }
+ }
+ sortRoutes(result_directory.children);
+ const result = [result_directory, ...result_routes];
+ if (_state.route === null) sortRoutes(result);
+ return result;
+}
+/**
+* Sorts the routes in the given result.
+* @param result The result to sort.
+*/
+function sortRoutes(result) {
+ result.sort((a, b) => {
+ if (a.specificity.type !== b.specificity.type) return a.specificity.type - b.specificity.type;
+ if (a.specificity.static_length !== b.specificity.static_length) return b.specificity.static_length - a.specificity.static_length;
+ if (a.specificity.position !== b.specificity.position) return a.specificity.position - b.specificity.position;
+ return 0;
+ });
+}
+
+//#endregion
+//#region src/router.ts
+/**
+* Creates new IttyRouter from filesystem.
+* @param path_root The path to scan.
+* @returns The new IttyRouter.
+*/
+function createRouter(path_root) {
+ const router = (0, itty_router.IttyRouter)();
+ fillRouter(readFiles(path_root), router);
+ return router;
+}
+/**
+* Attaches routes to IttyRouter.
+* @param routes The routes to attach.
+* @param router The IttyRouter to attach to.
+*/
+function fillRouter(routes, router) {
+ for (const route of routes) if ("method" in route) router[route.method](route.route, (request) => {
+ return {
+ async getHandler() {
+ return (await import(route.path)).default;
+ },
+ path: route.path,
+ args: request.params
+ };
+ });
+ else if ("children" in route) fillRouter(route.children, router);
+}
+/**
+* Fetches data from router.
+* @param router The router to fetch data from.
+* @param method The HTTP method.
+* @param path The path to fetch data from.
+* @returns The response.
+*/
+async function useRouter(router, method, path) {
+ const url = `file://${path}`;
+ const result = await router.fetch({
+ method,
+ url
+ });
+ if (result) return result;
+ return await router.fetch({
+ method: "UNKNOWN",
+ url
+ }) ? "INVALID" : "NOT_EXISTS";
+}
+
+//#endregion
+//#region src/main.ts
+const ENTRYPOINT_PATH = node_path.default.dirname(process.argv[1]);
+var HyperAPI = class {
+ router;
+ off;
+ constructor(driver, root = node_path.default.join(ENTRYPOINT_PATH, "hyper-api")) {
+ this.router = createRouter(root);
+ this.off = driver.on("request", async (event) => {
+ const [request_external, response] = await this.processRequest(event.detail.request);
+ if (this.hooks_response.length > 0) {
+ const promises = [];
+ for (const fn of this.hooks_response) try {
+ const result = fn({
+ ...event.detail.request,
+ ...request_external,
+ response
+ });
+ if (result instanceof Promise) promises.push(result.catch(console.error));
+ } catch (error) {
+ console.error(error);
+ }
+ await Promise.all(promises);
+ }
+ event.detail.callback(response);
+ });
+ }
+ hooks_before_router = [];
+ onBeforeRouter(fn) {
+ this.hooks_before_router.push(fn);
+ return this;
+ }
+ hooks_response = [];
+ onResponse(fn) {
+ this.hooks_response.push(fn);
+ return this;
+ }
+ async processRequest(request) {
+ const request_external = {};
+ try {
+ if (request.path.startsWith("/") !== true) request.path = `/${request.path}`;
+ for (const fn of this.hooks_before_router) {
+ const request_added = await fn({
+ ...request,
+ ...request_external
+ });
+ if (request_added !== void 0) Object.assign(request_external, request_added);
+ }
+ const router_response = await useRouter(this.router, request.method, request.path);
+ if (router_response === "INVALID") throw new HyperAPIUnknownMethodNotAllowedError();
+ if (router_response === "NOT_EXISTS") throw new HyperAPIUnknownMethodError();
+ if (require_record.hasCommonKeys(router_response.args, request.args)) throw new HyperAPIInvalidParametersError();
+ request.args = {
+ ...request.args,
+ ...router_response.args
+ };
+ const { response } = await (await router_response.getHandler())._run({
+ ...request,
+ ...request_external
+ });
+ if (isHyperAPIResponse(response) !== true) throw new TypeError(`Invalid response type from module ${router_response.path}. Expected Response, HyperAPIError, array, object or undefined.`);
+ return [request_external, response];
+ } catch (error) {
+ if (error instanceof HyperAPIError) return [request_external, error];
+ console.error(error);
+ return [request_external, new HyperAPIInternalError()];
+ }
+ }
+ module() {
+ return new HyperAPIModule();
+ }
+ destroy() {
+ this.off();
+ }
+};
+
+//#endregion
+exports.HyperAPI = HyperAPI;
+exports.HyperAPIAuthorizationError = HyperAPIAuthorizationError;
+exports.HyperAPIBusyError = HyperAPIBusyError;
+exports.HyperAPICaptchaError = HyperAPICaptchaError;
+exports.HyperAPIConfirmationError = HyperAPIConfirmationError;
+exports.HyperAPIError = HyperAPIError;
+exports.HyperAPIForbiddenError = HyperAPIForbiddenError;
+exports.HyperAPIInternalError = HyperAPIInternalError;
+exports.HyperAPIInvalidParametersError = HyperAPIInvalidParametersError;
+exports.HyperAPIMaintenanceError = HyperAPIMaintenanceError;
+exports.HyperAPIMethodNotAllowedError = HyperAPIMethodNotAllowedError;
+exports.HyperAPIOTPError = HyperAPIOTPError;
+exports.HyperAPIObjectsLimitError = HyperAPIObjectsLimitError;
+exports.HyperAPIRateLimitError = HyperAPIRateLimitError;
+exports.HyperAPIUnknownMethodError = HyperAPIUnknownMethodError;
+exports.HyperAPIUnknownMethodNotAllowedError = HyperAPIUnknownMethodNotAllowedError;
\ No newline at end of file
diff --git a/dist/main.d.cts b/dist/main.d.cts
new file mode 100644
index 0000000..fc05f5b
--- /dev/null
+++ b/dist/main.d.cts
@@ -0,0 +1,92 @@
+import { a as Extend, c as HyperAPIErrorData, d as BaseRecord, f as EmptyObject, l as HyperAPIRequest, n as HyperAPIResponse, o as Join, r as HyperAPIModule, s as HyperAPIError, t as HyperAPIDriver } from "./driver-DO8IdV_Q.cjs";
+import { Promisable } from "type-fest";
+
+//#region src/api-errors.d.ts
+declare class HyperAPIAuthorizationError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIInvalidParametersError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIInternalError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIForbiddenError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIUnknownMethodError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIUnknownMethodNotAllowedError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIObjectsLimitError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIRateLimitError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPICaptchaError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIBusyError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIConfirmationError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIOTPError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIMaintenanceError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIMethodNotAllowedError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+//#endregion
+//#region src/main.d.ts
+declare class HyperAPI {
+ private router;
+ private off;
+ constructor(driver: HyperAPIDriver, root?: string);
+ private hooks_before_router;
+ onBeforeRouter(fn: (request: Join) => Promisable): HyperAPI>;
+ private hooks_response;
+ onResponse(fn: (request: Join>) => Promisable): HyperAPI;
+ private processRequest;
+ module(): HyperAPIModule;
+ destroy(): void;
+}
+//#endregion
+export { HyperAPI, HyperAPIAuthorizationError, HyperAPIBusyError, HyperAPICaptchaError, HyperAPIConfirmationError, HyperAPIError, HyperAPIForbiddenError, HyperAPIInternalError, HyperAPIInvalidParametersError, HyperAPIMaintenanceError, HyperAPIMethodNotAllowedError, HyperAPIOTPError, HyperAPIObjectsLimitError, HyperAPIRateLimitError, HyperAPIUnknownMethodError, HyperAPIUnknownMethodNotAllowedError };
\ No newline at end of file
diff --git a/dist/main.d.ts b/dist/main.d.ts
new file mode 100644
index 0000000..b694d68
--- /dev/null
+++ b/dist/main.d.ts
@@ -0,0 +1,92 @@
+import { a as Extend, c as HyperAPIErrorData, d as BaseRecord, f as EmptyObject, l as HyperAPIRequest, n as HyperAPIResponse, o as Join, r as HyperAPIModule, s as HyperAPIError, t as HyperAPIDriver } from "./driver-D8sen_tB.js";
+import { Promisable } from "type-fest";
+
+//#region src/api-errors.d.ts
+declare class HyperAPIAuthorizationError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIInvalidParametersError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIInternalError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIForbiddenError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIUnknownMethodError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIUnknownMethodNotAllowedError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIObjectsLimitError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIRateLimitError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPICaptchaError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIBusyError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIConfirmationError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIOTPError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIMaintenanceError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+declare class HyperAPIMethodNotAllowedError extends HyperAPIError {
+ code: number;
+ description: string;
+ httpStatus: number;
+}
+//#endregion
+//#region src/main.d.ts
+declare class HyperAPI {
+ private router;
+ private off;
+ constructor(driver: HyperAPIDriver, root?: string);
+ private hooks_before_router;
+ onBeforeRouter(fn: (request: Join) => Promisable): HyperAPI>;
+ private hooks_response;
+ onResponse(fn: (request: Join>) => Promisable): HyperAPI;
+ private processRequest;
+ module(): HyperAPIModule;
+ destroy(): void;
+}
+//#endregion
+export { HyperAPI, HyperAPIAuthorizationError, HyperAPIBusyError, HyperAPICaptchaError, HyperAPIConfirmationError, HyperAPIError, HyperAPIForbiddenError, HyperAPIInternalError, HyperAPIInvalidParametersError, HyperAPIMaintenanceError, HyperAPIMethodNotAllowedError, HyperAPIOTPError, HyperAPIObjectsLimitError, HyperAPIRateLimitError, HyperAPIUnknownMethodError, HyperAPIUnknownMethodNotAllowedError };
\ No newline at end of file
diff --git a/dist/main.js b/dist/main.js
new file mode 100644
index 0000000..14e982d
--- /dev/null
+++ b/dist/main.js
@@ -0,0 +1,439 @@
+import { n as isRecord, t as hasCommonKeys } from "./record-BeJ2ZKAR.js";
+import nodePath from "node:path";
+import { IttyRouter } from "itty-router";
+import { readdirSync } from "node:fs";
+
+//#region src/error.ts
+var HyperAPIError = class extends Error {
+ /** The error code. */
+ code = 0;
+ /** The error description. */
+ description = "HyperAPI error";
+ /** The error data. */
+ data;
+ /** HTTP status code. */
+ httpStatus;
+ /** HTTP headers to return. */
+ httpHeaders;
+ constructor(data) {
+ super();
+ if (isRecord(data)) this.data = data;
+ }
+ get message() {
+ return `${this.description} (code ${this.code}).`;
+ }
+ /**
+ * Creates response object.
+ * @returns -
+ */
+ getResponse() {
+ const response = { code: this.code };
+ if (typeof this.description === "string") response.description = this.description;
+ if (this.data) response.data = this.data;
+ return response;
+ }
+};
+
+//#endregion
+//#region src/api-errors.ts
+var HyperAPIAuthorizationError = class extends HyperAPIError {
+ code = 1;
+ description = "Authorization error";
+ httpStatus = 401;
+};
+var HyperAPIInvalidParametersError = class extends HyperAPIError {
+ code = 2;
+ description = "One of the parameters specified was missing or invalid";
+ httpStatus = 400;
+};
+var HyperAPIInternalError = class extends HyperAPIError {
+ code = 3;
+ description = "Internal error";
+ httpStatus = 500;
+};
+var HyperAPIForbiddenError = class extends HyperAPIError {
+ code = 4;
+ description = "You do not have permission to perform this action";
+ httpStatus = 403;
+};
+var HyperAPIUnknownMethodError = class extends HyperAPIError {
+ code = 5;
+ description = "Unknown method called";
+ httpStatus = 404;
+};
+var HyperAPIUnknownMethodNotAllowedError = class extends HyperAPIError {
+ code = 5;
+ description = "Unknown method called";
+ httpStatus = 405;
+};
+var HyperAPIObjectsLimitError = class extends HyperAPIError {
+ code = 6;
+ description = "Too many objects requested";
+ httpStatus = 400;
+};
+var HyperAPIRateLimitError = class extends HyperAPIError {
+ code = 7;
+ description = "Rate limit exceeded";
+ httpStatus = 429;
+};
+var HyperAPICaptchaError = class extends HyperAPIError {
+ code = 8;
+ description = "Captcha required";
+ httpStatus = 428;
+};
+var HyperAPIBusyError = class extends HyperAPIError {
+ code = 10;
+ description = "Endpoint is busy";
+ httpStatus = 503;
+};
+var HyperAPIConfirmationError = class extends HyperAPIError {
+ code = 11;
+ description = "Confirmation required";
+ httpStatus = 409;
+};
+var HyperAPIOTPError = class extends HyperAPIError {
+ code = 12;
+ description = "One-time password required";
+ httpStatus = 401;
+};
+var HyperAPIMaintenanceError = class extends HyperAPIError {
+ code = 13;
+ description = "Endpoint is in maintenance mode";
+ httpStatus = 503;
+};
+var HyperAPIMethodNotAllowedError = class extends HyperAPIError {
+ code = 14;
+ description = "HTTP method not allowed";
+ httpStatus = 405;
+};
+
+//#endregion
+//#region src/module.ts
+var HyperAPIModule = class {
+ chain = [];
+ use(fn) {
+ this.chain.push(fn);
+ return this;
+ }
+ set(key, arg1) {
+ this.chain.push(async (request) => {
+ const value = typeof arg1 === "function" ? await arg1(request) : arg1;
+ if (value) return { [key]: value };
+ });
+ return this;
+ }
+ action(fn) {
+ this.chain.push(async (request) => {
+ const response = await fn(request);
+ if (response) return { response };
+ });
+ return this;
+ }
+ async _run(request) {
+ const request_result = request;
+ for (const fn of this.chain) {
+ const request_add = await fn(request_result);
+ if (request_add) Object.assign(request_result, request_add);
+ }
+ return request_result;
+ }
+};
+
+//#endregion
+//#region src/response.ts
+/**
+* Checks if the given value is a HyperAPIResponse.
+* @param response - The value to check.
+* @returns True if the value is a HyperAPIResponse, false otherwise.
+*/
+function isHyperAPIResponse(response) {
+ return response instanceof HyperAPIError || response instanceof Response || isRecord(response) || Array.isArray(response) || response === void 0;
+}
+
+//#endregion
+//#region src/router/filename.ts
+const RE_OPTIONAL_CATCH_ALL = /^\[\[\.\.\.([a-z_][\da-z_]*)\]\]$/i;
+const RE_GREEDY = /^\[\.\.\.([a-z_][\da-z_]*)\]$/i;
+/**
+* Returns specificity for route.
+*
+* Values:
+* - 0: static route (e.g. `/foo`)
+* - 1: route with parameter (e.g. `/foo-:id`)
+* - 2: route with optional parameter (e.g. `/foo-:id?`)
+* - 3: route with greedy parameter (e.g. `/foo-:id+`)
+* - 4: (NOT USED) route with wildcard (e.g. `/*`)
+* @param name -
+* @returns -
+*/
+function parseFilename(name) {
+ if (name === "index" || name.length === 0) return { self: true };
+ const match_optional_catch_all = RE_OPTIONAL_CATCH_ALL.exec(name);
+ if (match_optional_catch_all) return {
+ self: true,
+ route: {
+ part: `:${match_optional_catch_all[1]}+`,
+ specificity: {
+ type: 3,
+ static_length: 0
+ }
+ }
+ };
+ const match_greedy = RE_GREEDY.exec(name);
+ if (match_greedy) return { route: {
+ part: `:${match_greedy[1]}+`,
+ specificity: {
+ type: 3,
+ static_length: 0
+ }
+ } };
+ let has_optional = false;
+ let static_length = name.length;
+ const route_part = name.replaceAll(/(\[([a-z_][\da-z_]*)\]|\[\[([a-z_][\da-z_]*)\]\])([^\da-z_]|$)/gi, (...args) => {
+ static_length -= args[1].length;
+ if (args[3] !== void 0) {
+ has_optional = true;
+ return `:${args[3]}?${args[4]}`;
+ }
+ return `:${args[2]}${args[4]}`;
+ });
+ if (name !== route_part) return { route: {
+ part: route_part,
+ specificity: {
+ type: has_optional ? 2 : 1,
+ static_length
+ }
+ } };
+ if (name.includes("[") !== true) return { route: {
+ part: route_part,
+ specificity: {
+ type: 0,
+ static_length: 0
+ }
+ } };
+ throw new Error(`Invalid filename "${name}".`);
+}
+
+//#endregion
+//#region src/router/file-tree.ts
+const RE_EXT = /\.([cm]?[jt]s)$/i;
+const RE_METHOD = /\.(delete|get|head|options|patch|post|put)$/i;
+var WalkSpecificityPosition = /* @__PURE__ */ function(WalkSpecificityPosition$1) {
+ WalkSpecificityPosition$1[WalkSpecificityPosition$1["FILE_METHOD"] = 0] = "FILE_METHOD";
+ WalkSpecificityPosition$1[WalkSpecificityPosition$1["FILE_ALL"] = 1] = "FILE_ALL";
+ WalkSpecificityPosition$1[WalkSpecificityPosition$1["DIRECTORY"] = 2] = "DIRECTORY";
+ return WalkSpecificityPosition$1;
+}(WalkSpecificityPosition || {});
+/**
+* Reads the file system and returns a tree of routes sorted correctly.
+* @param path_given The directory to read.
+* @param _state Internal state.
+* @returns -
+*/
+function readFiles(path_given, _state) {
+ _state ??= {
+ route: null,
+ specificity: {
+ type: 0,
+ static_length: 0,
+ position: WalkSpecificityPosition.DIRECTORY
+ }
+ };
+ const result_directory = {
+ specificity: _state.specificity,
+ children: []
+ };
+ const result_routes = [];
+ const entries = readdirSync(path_given, { withFileTypes: true });
+ if (process.env.NODE_ENV === "test") for (let i = entries.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [entries[i], entries[j]] = [entries[j], entries[i]];
+ }
+ for (const entry of entries) {
+ const path = nodePath.join(entry.parentPath, entry.name);
+ if (entry.isFile()) {
+ let name = entry.name.replace(RE_EXT, "");
+ if (name.endsWith(".test")) continue;
+ let method = "all";
+ name = name.replace(RE_METHOD, (_, g1) => {
+ method = g1.toLowerCase();
+ return "";
+ });
+ const { self, route } = parseFilename(name);
+ const specificity_position = method === "all" ? WalkSpecificityPosition.FILE_ALL : WalkSpecificityPosition.FILE_METHOD;
+ if (self) result_routes.push({
+ specificity: {
+ ..._state.specificity,
+ position: specificity_position
+ },
+ method,
+ route: _state.route ?? "/",
+ path
+ });
+ if (route) result_directory.children.push({
+ specificity: {
+ ...route.specificity,
+ position: specificity_position
+ },
+ method,
+ route: (_state.route ?? "") + nodePath.sep + route.part,
+ path
+ });
+ } else if (entry.isDirectory()) {
+ const { self, route } = parseFilename(entry.name);
+ if (self) throw new Error(`Invalid directory name "${entry.name}" at "${path}". Can not use optional catch-all in directory name.`);
+ if (!route) throw new Error(`Invalid directory name "${entry.name}" at "${path}".`);
+ result_directory.children.push(...readFiles(path, {
+ route: (_state.route ?? "") + nodePath.sep + route.part,
+ specificity: {
+ ...route.specificity,
+ position: WalkSpecificityPosition.DIRECTORY
+ }
+ }));
+ }
+ }
+ sortRoutes(result_directory.children);
+ const result = [result_directory, ...result_routes];
+ if (_state.route === null) sortRoutes(result);
+ return result;
+}
+/**
+* Sorts the routes in the given result.
+* @param result The result to sort.
+*/
+function sortRoutes(result) {
+ result.sort((a, b) => {
+ if (a.specificity.type !== b.specificity.type) return a.specificity.type - b.specificity.type;
+ if (a.specificity.static_length !== b.specificity.static_length) return b.specificity.static_length - a.specificity.static_length;
+ if (a.specificity.position !== b.specificity.position) return a.specificity.position - b.specificity.position;
+ return 0;
+ });
+}
+
+//#endregion
+//#region src/router.ts
+/**
+* Creates new IttyRouter from filesystem.
+* @param path_root The path to scan.
+* @returns The new IttyRouter.
+*/
+function createRouter(path_root) {
+ const router = IttyRouter();
+ fillRouter(readFiles(path_root), router);
+ return router;
+}
+/**
+* Attaches routes to IttyRouter.
+* @param routes The routes to attach.
+* @param router The IttyRouter to attach to.
+*/
+function fillRouter(routes, router) {
+ for (const route of routes) if ("method" in route) router[route.method](route.route, (request) => {
+ return {
+ async getHandler() {
+ return (await import(route.path)).default;
+ },
+ path: route.path,
+ args: request.params
+ };
+ });
+ else if ("children" in route) fillRouter(route.children, router);
+}
+/**
+* Fetches data from router.
+* @param router The router to fetch data from.
+* @param method The HTTP method.
+* @param path The path to fetch data from.
+* @returns The response.
+*/
+async function useRouter(router, method, path) {
+ const url = `file://${path}`;
+ const result = await router.fetch({
+ method,
+ url
+ });
+ if (result) return result;
+ return await router.fetch({
+ method: "UNKNOWN",
+ url
+ }) ? "INVALID" : "NOT_EXISTS";
+}
+
+//#endregion
+//#region src/main.ts
+const ENTRYPOINT_PATH = nodePath.dirname(process.argv[1]);
+var HyperAPI = class {
+ router;
+ off;
+ constructor(driver, root = nodePath.join(ENTRYPOINT_PATH, "hyper-api")) {
+ this.router = createRouter(root);
+ this.off = driver.on("request", async (event) => {
+ const [request_external, response] = await this.processRequest(event.detail.request);
+ if (this.hooks_response.length > 0) {
+ const promises = [];
+ for (const fn of this.hooks_response) try {
+ const result = fn({
+ ...event.detail.request,
+ ...request_external,
+ response
+ });
+ if (result instanceof Promise) promises.push(result.catch(console.error));
+ } catch (error) {
+ console.error(error);
+ }
+ await Promise.all(promises);
+ }
+ event.detail.callback(response);
+ });
+ }
+ hooks_before_router = [];
+ onBeforeRouter(fn) {
+ this.hooks_before_router.push(fn);
+ return this;
+ }
+ hooks_response = [];
+ onResponse(fn) {
+ this.hooks_response.push(fn);
+ return this;
+ }
+ async processRequest(request) {
+ const request_external = {};
+ try {
+ if (request.path.startsWith("/") !== true) request.path = `/${request.path}`;
+ for (const fn of this.hooks_before_router) {
+ const request_added = await fn({
+ ...request,
+ ...request_external
+ });
+ if (request_added !== void 0) Object.assign(request_external, request_added);
+ }
+ const router_response = await useRouter(this.router, request.method, request.path);
+ if (router_response === "INVALID") throw new HyperAPIUnknownMethodNotAllowedError();
+ if (router_response === "NOT_EXISTS") throw new HyperAPIUnknownMethodError();
+ if (hasCommonKeys(router_response.args, request.args)) throw new HyperAPIInvalidParametersError();
+ request.args = {
+ ...request.args,
+ ...router_response.args
+ };
+ const { response } = await (await router_response.getHandler())._run({
+ ...request,
+ ...request_external
+ });
+ if (isHyperAPIResponse(response) !== true) throw new TypeError(`Invalid response type from module ${router_response.path}. Expected Response, HyperAPIError, array, object or undefined.`);
+ return [request_external, response];
+ } catch (error) {
+ if (error instanceof HyperAPIError) return [request_external, error];
+ console.error(error);
+ return [request_external, new HyperAPIInternalError()];
+ }
+ }
+ module() {
+ return new HyperAPIModule();
+ }
+ destroy() {
+ this.off();
+ }
+};
+
+//#endregion
+export { HyperAPI, HyperAPIAuthorizationError, HyperAPIBusyError, HyperAPICaptchaError, HyperAPIConfirmationError, HyperAPIError, HyperAPIForbiddenError, HyperAPIInternalError, HyperAPIInvalidParametersError, HyperAPIMaintenanceError, HyperAPIMethodNotAllowedError, HyperAPIOTPError, HyperAPIObjectsLimitError, HyperAPIRateLimitError, HyperAPIUnknownMethodError, HyperAPIUnknownMethodNotAllowedError };
\ No newline at end of file
diff --git a/dist/record-BeJ2ZKAR.js b/dist/record-BeJ2ZKAR.js
new file mode 100644
index 0000000..6ef675a
--- /dev/null
+++ b/dist/record-BeJ2ZKAR.js
@@ -0,0 +1,22 @@
+//#region src/utils/record.ts
+/**
+* Check if a value is a record.
+* @param value -
+* @returns -
+*/
+function isRecord(value) {
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value.constructor === Object && Object.prototype.toString.call(value) === "[object Object]";
+}
+/**
+* Checks if there are common keys in both object.
+* @param value1 -
+* @param value2 -
+* @returns -
+*/
+function hasCommonKeys(value1, value2) {
+ for (const key of Object.keys(value2)) if (Object.hasOwn(value1, key)) return true;
+ return false;
+}
+
+//#endregion
+export { isRecord as n, hasCommonKeys as t };
\ No newline at end of file
diff --git a/dist/record-D97pjgdq.cjs b/dist/record-D97pjgdq.cjs
new file mode 100644
index 0000000..6432d49
--- /dev/null
+++ b/dist/record-D97pjgdq.cjs
@@ -0,0 +1,63 @@
+//#region rolldown:runtime
+var __create = Object.create;
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __getProtoOf = Object.getPrototypeOf;
+var __hasOwnProp = Object.prototype.hasOwnProperty;
+var __copyProps = (to, from, except, desc) => {
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
+ key = keys[i];
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
+ get: ((k) => from[k]).bind(null, key),
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
+ });
+ }
+ return to;
+};
+var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
+ value: mod,
+ enumerable: true
+}) : target, mod));
+
+//#endregion
+
+//#region src/utils/record.ts
+/**
+* Check if a value is a record.
+* @param value -
+* @returns -
+*/
+function isRecord(value) {
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value.constructor === Object && Object.prototype.toString.call(value) === "[object Object]";
+}
+/**
+* Checks if there are common keys in both object.
+* @param value1 -
+* @param value2 -
+* @returns -
+*/
+function hasCommonKeys(value1, value2) {
+ for (const key of Object.keys(value2)) if (Object.hasOwn(value1, key)) return true;
+ return false;
+}
+
+//#endregion
+Object.defineProperty(exports, '__toESM', {
+ enumerable: true,
+ get: function () {
+ return __toESM;
+ }
+});
+Object.defineProperty(exports, 'hasCommonKeys', {
+ enumerable: true,
+ get: function () {
+ return hasCommonKeys;
+ }
+});
+Object.defineProperty(exports, 'isRecord', {
+ enumerable: true,
+ get: function () {
+ return isRecord;
+ }
+});
\ No newline at end of file
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..32edb10
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,6 @@
+import { configCommon } from '@kirick/lint/eslint/common';
+import { configNode } from '@kirick/lint/eslint/node';
+import { configOxlint } from '@kirick/lint/eslint/oxlint';
+import { defineConfig } from 'eslint/config';
+
+export default defineConfig([...configCommon, ...configNode, ...configOxlint]);
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..76701b7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "@hyperapi/core",
+ "version": "0.5.0-dev",
+ "description": "Core package of the HyperAPI project.",
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "main": "dist/main.js",
+ "exports": {
+ ".": {
+ "types": {
+ "import": "./dist/main.d.ts",
+ "require": "./dist/main.d.cts"
+ },
+ "import": "./dist/main.js",
+ "require": "./dist/main.cjs"
+ },
+ "./dev": {
+ "types": {
+ "import": "./dist/dev.d.ts",
+ "require": "./dist/dev.d.cts"
+ },
+ "import": "./dist/dev.js",
+ "require": "./dist/dev.cjs"
+ }
+ },
+ "engines": {
+ "node": ">=21"
+ },
+ "dependencies": {
+ "itty-router": "5.0.22",
+ "neoevents": "0.3.0",
+ "type-fest": "4.41.0"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "2.3.1",
+ "@kirick/lint": "0.2.12",
+ "@types/bun": "1.3.1",
+ "eslint": "9.38.0",
+ "oxlint": "1.24.0",
+ "publint": "0.3.15",
+ "tsdown": "0.15.12",
+ "typescript": "5.9.3",
+ "unplugin-unused": "0.5.4",
+ "valibot": "1.1.0",
+ "vitest": "4.0.4"
+ },
+ "scripts": {
+ "build": "tsdown src/main.ts src/dev.ts --clean --tsconfig tsconfig.main.json --publint --unused --dts --format esm --format cjs",
+ "check": "bun run lint && bun run build && bun run test",
+ "lint": "biome format && oxlint && eslint . && tsc -b",
+ "test": "npm run test:vitest && bun test --coverage",
+ "test:vitest": "vitest run --no-file-parallelism"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/hyperapi/core.git"
+ },
+ "keywords": [
+ "hyperapi",
+ "api"
+ ],
+ "author": "Daniil Kirichenko (https://twitter.com/kirickme)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/hyperapi/core/issues"
+ },
+ "homepage": "https://github.com/hyperapi/core#readme"
+}
diff --git a/src/api-errors.ts b/src/api-errors.ts
new file mode 100644
index 0000000..3e54602
--- /dev/null
+++ b/src/api-errors.ts
@@ -0,0 +1,114 @@
+import { HyperAPIError, type HyperAPIErrorData } from './error.js';
+
+export class HyperAPIAuthorizationError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 1;
+ override description = 'Authorization error';
+ override httpStatus = 401; // Unauthorized
+}
+
+export class HyperAPIInvalidParametersError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 2;
+ override description =
+ 'One of the parameters specified was missing or invalid';
+ override httpStatus = 400; // Bad Request
+}
+
+export class HyperAPIInternalError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 3;
+ override description = 'Internal error';
+ override httpStatus = 500; // Internal Server Error
+}
+
+export class HyperAPIForbiddenError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 4;
+ override description = 'You do not have permission to perform this action';
+ override httpStatus = 403; // Forbidden
+}
+
+export class HyperAPIUnknownMethodError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 5;
+ override description = 'Unknown method called';
+ override httpStatus = 404; // Not Found
+}
+
+export class HyperAPIUnknownMethodNotAllowedError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 5;
+ override description = 'Unknown method called';
+ override httpStatus = 405; // Method Not Allowed
+}
+
+export class HyperAPIObjectsLimitError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 6;
+ override description = 'Too many objects requested';
+ override httpStatus = 400; // Bad Request
+}
+
+export class HyperAPIRateLimitError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 7;
+ override description = 'Rate limit exceeded';
+ override httpStatus = 429; // Too Many Requests
+}
+
+export class HyperAPICaptchaError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 8;
+ override description = 'Captcha required';
+ override httpStatus = 428; // Precondition Required
+}
+
+export class HyperAPIBusyError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 10;
+ override description = 'Endpoint is busy';
+ override httpStatus = 503; // Service Unavailable
+}
+
+export class HyperAPIConfirmationError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 11;
+ override description = 'Confirmation required';
+ override httpStatus = 409; // Conflict
+}
+
+export class HyperAPIOTPError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 12;
+ override description = 'One-time password required';
+ override httpStatus = 401; // Unauthorized
+}
+
+export class HyperAPIMaintenanceError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 13;
+ override description = 'Endpoint is in maintenance mode';
+ override httpStatus = 503; // Service Unavailable
+}
+
+export class HyperAPIMethodNotAllowedError<
+ D extends HyperAPIErrorData,
+> extends HyperAPIError {
+ override code = 14;
+ override description = 'HTTP method not allowed';
+ override httpStatus = 405; // Method Not Allowed
+}
diff --git a/src/dev.ts b/src/dev.ts
new file mode 100644
index 0000000..bd5398d
--- /dev/null
+++ b/src/dev.ts
@@ -0,0 +1,14 @@
+export { HyperAPIDriver } from './driver.js';
+export type { HyperAPIErrorData } from './error.js';
+export type {
+ HyperAPIModule,
+ HyperAPIModuleResponse,
+} from './module.js';
+export type {
+ HyperAPIRequest,
+ HyperAPIRequestArgs,
+} from './request.js';
+export type { HyperAPIResponse } from './response.js';
+export { type HyperAPIMethod, isHyperApiMethod } from './utils/methods.js';
+export type { BaseRecord, EmptyObject } from './utils/record.js';
+export { isRecord } from './utils/record.js';
diff --git a/src/driver.ts b/src/driver.ts
new file mode 100644
index 0000000..32b4845
--- /dev/null
+++ b/src/driver.ts
@@ -0,0 +1,27 @@
+import { NeoEvent, NeoEventTarget } from 'neoevents';
+import type { HyperAPIRequest } from './request.js';
+import type { HyperAPIResponse } from './response.js';
+
+export class HyperAPIDriver<
+ R extends HyperAPIRequest = HyperAPIRequest,
+> extends NeoEventTarget<{
+ request: NeoEvent<{
+ request: R;
+ callback: (response: HyperAPIResponse) => void;
+ }>;
+}> {
+ // declare R: R;
+
+ protected emitRequest(request: R): Promise {
+ return new Promise((resolve) => {
+ this.emit('request', {
+ request,
+ callback: resolve,
+ });
+ });
+ }
+
+ // override destroy(): void {
+ // this.destroy();
+ // }
+}
diff --git a/src/error.test.ts b/src/error.test.ts
new file mode 100644
index 0000000..1feadd9
--- /dev/null
+++ b/src/error.test.ts
@@ -0,0 +1,21 @@
+import { expect, test } from 'vitest';
+import { HyperAPIBusyError } from './api-errors.js';
+import { HyperAPIError } from './error.js';
+
+test('data', () => {
+ const error = new HyperAPIBusyError({
+ foo: 'bar',
+ });
+ expect(error.data?.foo).toBe('bar');
+});
+
+// oxlint-disable-next-line typescript/no-explicit-any
+class HyperAPICustomError extends HyperAPIError {
+ override code = 1001;
+ override description = 'This is a custom error';
+}
+
+test('custom error', () => {
+ const error = new HyperAPICustomError();
+ expect(error.message).toBe('This is a custom error (code 1001).');
+});
diff --git a/src/error.ts b/src/error.ts
new file mode 100644
index 0000000..83bb45b
--- /dev/null
+++ b/src/error.ts
@@ -0,0 +1,56 @@
+import { isRecord } from './utils/record.js';
+
+export type HyperAPIErrorData = Record | undefined;
+
+interface HyperAPIErrorResponse {
+ code: number;
+ description?: string;
+ data?: HyperAPIErrorData;
+}
+
+export class HyperAPIError<
+ D extends HyperAPIErrorData = undefined,
+> extends Error {
+ /** The error code. */
+ readonly code: number = 0;
+ /** The error description. */
+ readonly description: string = 'HyperAPI error';
+ /** The error data. */
+ readonly data?: D;
+ /** HTTP status code. */
+ readonly httpStatus?: number;
+ /** HTTP headers to return. */
+ readonly httpHeaders?: Record;
+
+ constructor(data?: D) {
+ super();
+
+ if (isRecord(data)) {
+ this.data = data;
+ }
+ }
+
+ override get message() {
+ return `${this.description} (code ${this.code}).`;
+ }
+
+ /**
+ * Creates response object.
+ * @returns -
+ */
+ getResponse(): HyperAPIErrorResponse {
+ const response: HyperAPIErrorResponse = {
+ code: this.code,
+ };
+
+ if (typeof this.description === 'string') {
+ response.description = this.description;
+ }
+
+ if (this.data) {
+ response.data = this.data;
+ }
+
+ return response;
+ }
+}
diff --git a/src/main.test.ts b/src/main.test.ts
new file mode 100644
index 0000000..f4eca4d
--- /dev/null
+++ b/src/main.test.ts
@@ -0,0 +1,191 @@
+// oxlint-disable max-lines-per-function, max-nested-callbacks
+
+import { describe, expect, expectTypeOf, test, vi } from 'vitest';
+import { HyperAPITestDriver, type TestRequest } from '../test/driver.js';
+import { HyperAPI } from './main.js';
+import type { HyperAPIResponse } from './response.js';
+
+test('HyperAPI constructor', () => {
+ const driver = new HyperAPITestDriver();
+ const hyperApi = new HyperAPI(
+ driver,
+ new URL('../test/hyper-api', import.meta.url).pathname,
+ );
+
+ expect(hyperApi).toBeInstanceOf(HyperAPI);
+
+ hyperApi.destroy();
+ driver.destroy();
+});
+
+describe('hooks', () => {
+ describe('onBeforeRouter', () => {
+ test('run & pass props', async () => {
+ const mockFnFirst = vi.fn(() => null);
+ const mockFnSecond = vi.fn(() => null);
+
+ const driver = new HyperAPITestDriver();
+ const hyperApi = new HyperAPI(
+ driver,
+ new URL('../test/hyper-api', import.meta.url).pathname,
+ )
+ .onBeforeRouter((request) => {
+ expectTypeOf(request).toExtend();
+ expectTypeOf(request.foo).toBeString();
+
+ mockFnFirst();
+
+ return { external: 1 };
+ })
+ .onBeforeRouter((request) => {
+ expectTypeOf(request).toExtend();
+ expectTypeOf(request.external).toBeNumber();
+
+ mockFnSecond();
+
+ expect(request.external).toBe(1);
+ });
+
+ expect(hyperApi).toBeInstanceOf(HyperAPI);
+
+ const [success, response] = await driver.trigger('GET', '/module/main');
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ external: 1,
+ });
+
+ expect(mockFnFirst).toHaveBeenCalledOnce();
+ expect(mockFnSecond).toHaveBeenCalledOnce();
+
+ hyperApi.destroy();
+ driver.destroy();
+ });
+
+ test('throw', async () => {
+ const mockFn = vi.fn(() => null);
+
+ const driver = new HyperAPITestDriver();
+ const hyperApi = new HyperAPI(
+ driver,
+ new URL('../test/hyper-api', import.meta.url).pathname,
+ ).onBeforeRouter((request) => {
+ expectTypeOf(request).toExtend();
+ expectTypeOf(request.foo).toBeString();
+
+ mockFn();
+
+ throw new Error('test');
+ });
+
+ const [success, response] = await driver.trigger('GET', '/router');
+ expect(success).toBe(false);
+ expect(response).toStrictEqual({
+ code: 3,
+ description: 'Internal error',
+ });
+
+ expect(mockFn).toHaveBeenCalledOnce();
+
+ hyperApi.destroy();
+ driver.destroy();
+ });
+ });
+
+ describe('onResponse', () => {
+ test('run at once', async () => {
+ const mockFnFirst = vi.fn(() => Date.now());
+ let valueFirst: number = Number.NEGATIVE_INFINITY;
+ const mockFnSecond = vi.fn(() => Date.now());
+ let valueSecond: number = Number.POSITIVE_INFINITY;
+
+ const driver = new HyperAPITestDriver();
+ const hyperApi = new HyperAPI(
+ driver,
+ new URL('../test/hyper-api', import.meta.url).pathname,
+ )
+ .onResponse(async (request) => {
+ expectTypeOf(request).toExtend();
+ expectTypeOf(request.foo).toBeString();
+ expectTypeOf(request.response).toEqualTypeOf();
+ expect(request.response).toStrictEqual({
+ path: 'router/index.get.ts',
+ method: 'GET',
+ args: {},
+ });
+
+ valueFirst = mockFnFirst();
+
+ await new Promise((resolve) => {
+ setTimeout(resolve, 100);
+ });
+ })
+ .onResponse((request) => {
+ expectTypeOf(request).toExtend();
+ expectTypeOf(request.foo).toBeString();
+ expectTypeOf(request.response).toEqualTypeOf();
+ expect(request.response).toStrictEqual({
+ path: 'router/index.get.ts',
+ method: 'GET',
+ args: {},
+ });
+
+ valueSecond = mockFnSecond();
+ });
+
+ expect(hyperApi).toBeInstanceOf(HyperAPI);
+ const [success] = await driver.trigger('GET', '/router');
+ expect(success).toBe(true);
+ expect(mockFnFirst).toHaveBeenCalledOnce();
+ expect(mockFnSecond).toHaveBeenCalledOnce();
+ expect(valueSecond - valueFirst).toBeLessThanOrEqual(100);
+
+ hyperApi.destroy();
+ driver.destroy();
+ });
+
+ describe('throw', () => {
+ test('sync', async () => {
+ const mockFn = vi.fn(() => null);
+
+ const driver = new HyperAPITestDriver();
+ const hyperApi = new HyperAPI(
+ driver,
+ new URL('../test/hyper-api', import.meta.url).pathname,
+ ).onResponse(() => {
+ mockFn();
+ throw new Error('test');
+ });
+
+ const [success] = await driver.trigger('GET', '/router');
+ expect(success).toBe(true);
+
+ expect(mockFn).toHaveBeenCalledOnce();
+
+ hyperApi.destroy();
+ driver.destroy();
+ });
+
+ test('async', async () => {
+ const mockFn = vi.fn(() => null);
+
+ const driver = new HyperAPITestDriver();
+ const hyperApi = new HyperAPI(
+ driver,
+ new URL('../test/hyper-api', import.meta.url).pathname,
+ ).onResponse(async () => {
+ await Promise.resolve();
+ mockFn();
+ throw new Error('test');
+ });
+
+ const [success] = await driver.trigger('GET', '/router');
+ expect(success).toBe(true);
+
+ expect(mockFn).toHaveBeenCalledOnce();
+
+ hyperApi.destroy();
+ driver.destroy();
+ });
+ });
+ });
+});
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..9fca57f
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,183 @@
+import nodePath from 'node:path';
+import type { Promisable } from 'type-fest';
+import {
+ HyperAPIInternalError,
+ HyperAPIInvalidParametersError,
+ HyperAPIUnknownMethodError,
+ HyperAPIUnknownMethodNotAllowedError,
+} from './api-errors.js';
+import type { HyperAPIDriver } from './driver.js';
+import { HyperAPIError } from './error.js';
+import { HyperAPIModule } from './module.js';
+import type { HyperAPIRequest } from './request.js';
+import { type HyperAPIResponse, isHyperAPIResponse } from './response.js';
+import { createRouter, useRouter } from './router.js';
+import {
+ type BaseRecord,
+ type EmptyObject,
+ hasCommonKeys,
+} from './utils/record.js';
+import type { Extend, Join } from './utils/types.js';
+
+const ENTRYPOINT_PATH = nodePath.dirname(process.argv[1]!);
+
+export class HyperAPI<
+ Req extends HyperAPIRequest,
+ ReqExtra extends BaseRecord = EmptyObject,
+> {
+ private router;
+ private off: () => void;
+
+ constructor(
+ driver: HyperAPIDriver,
+ root: string = nodePath.join(ENTRYPOINT_PATH, 'hyper-api'),
+ ) {
+ this.router = createRouter(root);
+
+ this.off = driver.on('request', async (event) => {
+ const [request_external, response] = await this.processRequest(
+ event.detail.request,
+ );
+
+ if (this.hooks_response.length > 0) {
+ const promises = [];
+ for (const fn of this.hooks_response) {
+ // catch sync errors
+ try {
+ const result = fn({
+ ...event.detail.request,
+ ...request_external,
+ response,
+ });
+
+ if (result instanceof Promise) {
+ // oxlint-disable-next-line no-console
+ promises.push(result.catch(console.error));
+ }
+ } catch (error) {
+ // oxlint-disable-next-line no-console
+ console.error(error);
+ }
+ }
+
+ await Promise.all(promises);
+ }
+
+ event.detail.callback(response);
+ });
+ }
+
+ private hooks_before_router: ((
+ request: Join,
+ ) => Promisable)[] = [];
+
+ onBeforeRouter(
+ fn: (request: Join) => Promisable,
+ ) {
+ this.hooks_before_router.push(fn);
+
+ return this as unknown as HyperAPI>;
+ }
+
+ private hooks_response: Parameters[0][] = [];
+
+ onResponse(
+ fn: (
+ request: Join>,
+ ) => Promisable,
+ ) {
+ this.hooks_response.push(fn);
+
+ return this as HyperAPI;
+ }
+
+ private async processRequest(
+ request: Req,
+ ): Promise<[ReqExtra, HyperAPIResponse]> {
+ // 1. *Driver* creates a request and passes it to the *Core*
+
+ // console.log('Processing request:', request);
+
+ const request_external = {} as ReqExtra;
+
+ try {
+ if (request.path.startsWith('/') !== true) {
+ request.path = `/${request.path}`;
+ }
+
+ // 2. *Core* executes all registered `onBeforeRouter` hooks...
+ for (const fn of this.hooks_before_router) {
+ // oxlint-disable-next-line no-await-in-loop
+ const request_added = await fn({
+ ...request,
+ ...request_external,
+ });
+ if (request_added !== undefined) {
+ Object.assign(request_external, request_added);
+ }
+ }
+
+ // 3. *Core* uses a file router...
+ const router_response = await useRouter(
+ this.router,
+ request.method,
+ request.path,
+ );
+
+ if (router_response === 'INVALID') {
+ throw new HyperAPIUnknownMethodNotAllowedError();
+ }
+
+ if (router_response === 'NOT_EXISTS') {
+ throw new HyperAPIUnknownMethodError();
+ }
+
+ // 4. *Core* merges arguments received from the driver with arguments extracted from the request path by the file router
+ if (hasCommonKeys(router_response.args, request.args)) {
+ throw new HyperAPIInvalidParametersError();
+ }
+
+ request.args = {
+ ...request.args,
+ ...router_response.args,
+ } as Req['args'];
+
+ // 5. *Core* imports the matched module file...
+ const handler = await router_response.getHandler();
+
+ // 6. *Core* calls the module
+ const { response } = await handler._run({
+ ...request,
+ ...request_external,
+ });
+ if (isHyperAPIResponse(response) !== true) {
+ throw new TypeError(
+ `Invalid response type from module ${router_response.path}. Expected Response, HyperAPIError, array, object or undefined.`,
+ );
+ }
+
+ return [request_external, response];
+ } catch (error) {
+ if (error instanceof HyperAPIError) {
+ return [request_external, error];
+ }
+
+ // oxlint-disable-next-line no-console
+ console.error(error);
+
+ return [request_external, new HyperAPIInternalError()];
+ }
+ }
+
+ // oxlint-disable-next-line class-methods-use-this
+ module(): HyperAPIModule {
+ return new HyperAPIModule();
+ }
+
+ destroy(): void {
+ this.off();
+ }
+}
+
+export * from './api-errors.js';
+export { HyperAPIError } from './error.js';
diff --git a/src/module.ts b/src/module.ts
new file mode 100644
index 0000000..5c5ffca
--- /dev/null
+++ b/src/module.ts
@@ -0,0 +1,80 @@
+import type { Promisable } from 'type-fest';
+import type { HyperAPIRequest } from './request.js';
+import type { BaseRecord } from './utils/record.js';
+import type { Extend, Join } from './utils/types.js';
+
+export type HyperAPIModuleResponse =
+ | Response
+ | BaseRecord
+ | unknown[]
+ | undefined;
+
+export class HyperAPIModule<
+ Req extends HyperAPIRequest,
+ ReqExtra extends BaseRecord = never,
+> {
+ private chain: ((
+ request: Join,
+ ) => Promisable)[] = [];
+
+ use(
+ fn: (request: Join) => Promisable,
+ ) {
+ this.chain.push(fn);
+
+ return this as unknown as HyperAPIModule>;
+ }
+
+ set(
+ key: K,
+ fn: (request: Join) => Promisable,
+ ): HyperAPIModule>;
+ set(
+ key: K,
+ value: V,
+ ): HyperAPIModule>;
+ set(key: K, arg1: unknown) {
+ this.chain.push(async (request) => {
+ const value = typeof arg1 === 'function' ? await arg1(request) : arg1;
+ if (value) {
+ return { [key]: value };
+ }
+ });
+
+ return this as unknown as HyperAPIModule<
+ Req,
+ Extend
+ >;
+ }
+
+ action(
+ fn: (request: Join) => Promisable,
+ ) {
+ this.chain.push(async (request) => {
+ const response = await fn(request);
+ if (response) {
+ return { response };
+ }
+ });
+
+ return this as unknown as HyperAPIModule<
+ Req,
+ Extend
+ >;
+ }
+
+ async _run(
+ request: Req,
+ ): Promise>> {
+ const request_result = request;
+ for (const fn of this.chain) {
+ // oxlint-disable-next-line no-await-in-loop
+ const request_add = await fn(request_result as Join);
+ if (request_add) {
+ Object.assign(request_result, request_add);
+ }
+ }
+
+ return request_result as Awaited>;
+ }
+}
diff --git a/src/request.ts b/src/request.ts
new file mode 100644
index 0000000..9b86213
--- /dev/null
+++ b/src/request.ts
@@ -0,0 +1,10 @@
+import type { HyperAPIMethod } from './utils/methods.js';
+import type { BaseRecord, EmptyObject } from './utils/record.js';
+
+export type HyperAPIRequestArgs = BaseRecord;
+
+export interface HyperAPIRequest {
+ method: HyperAPIMethod;
+ path: string;
+ args: A;
+}
diff --git a/src/response.ts b/src/response.ts
new file mode 100644
index 0000000..f86f2c4
--- /dev/null
+++ b/src/response.ts
@@ -0,0 +1,23 @@
+import { HyperAPIError } from './error.js';
+import type { HyperAPIModuleResponse } from './module.js';
+import { isRecord } from './utils/record.js';
+
+// oxlint-disable-next-line typescript/no-explicit-any
+export type HyperAPIResponse = HyperAPIModuleResponse | HyperAPIError;
+
+/**
+ * Checks if the given value is a HyperAPIResponse.
+ * @param response - The value to check.
+ * @returns True if the value is a HyperAPIResponse, false otherwise.
+ */
+export function isHyperAPIResponse(
+ response: unknown,
+): response is HyperAPIResponse {
+ return (
+ response instanceof HyperAPIError
+ || response instanceof Response
+ || isRecord(response)
+ || Array.isArray(response)
+ || response === undefined
+ );
+}
diff --git a/src/router.ts b/src/router.ts
new file mode 100644
index 0000000..d153241
--- /dev/null
+++ b/src/router.ts
@@ -0,0 +1,93 @@
+import { type IRequest, IttyRouter, type IttyRouterType } from 'itty-router';
+import type { HyperAPIModule } from './module.js';
+import type { HyperAPIRequest } from './request.js';
+import { readFiles, type WalkResult } from './router/file-tree.js';
+import type { HyperAPIMethod } from './utils/methods.js';
+import type { BaseRecord } from './utils/record.js';
+
+type HyperAPIIttyRouterResponse = {
+ getHandler: () => Promise>>;
+ path: string;
+ args: BaseRecord;
+};
+type HyperAPIIttyRouter = IttyRouterType<
+ IRequest,
+ [],
+ HyperAPIIttyRouterResponse
+>;
+
+/**
+ * Creates new IttyRouter from filesystem.
+ * @param path_root The path to scan.
+ * @returns The new IttyRouter.
+ */
+export function createRouter(path_root: string): HyperAPIIttyRouter {
+ // oxlint-disable-next-line new-cap
+ const router: HyperAPIIttyRouter = IttyRouter();
+
+ const routes = readFiles(path_root);
+ // console.log(Bun.inspect(routes, { colors: true }));
+
+ fillRouter(routes, router);
+
+ return router;
+}
+
+/**
+ * Attaches routes to IttyRouter.
+ * @param routes The routes to attach.
+ * @param router The IttyRouter to attach to.
+ */
+function fillRouter(routes: WalkResult, router: HyperAPIIttyRouter) {
+ for (const route of routes) {
+ if ('method' in route) {
+ // console.log('[fillRouter]', route.method, route.route);
+ router[route.method](route.route, (request) => {
+ const r_ = {
+ async getHandler() {
+ const module_ = await import(route.path);
+ return module_.default;
+ },
+ path: route.path,
+ args: request.params,
+ } as HyperAPIIttyRouterResponse;
+
+ return r_;
+ });
+ } else if ('children' in route) {
+ fillRouter(route.children, router);
+ }
+ }
+}
+
+/**
+ * Fetches data from router.
+ * @param router The router to fetch data from.
+ * @param method The HTTP method.
+ * @param path The path to fetch data from.
+ * @returns The response.
+ */
+export async function useRouter(
+ router: HyperAPIIttyRouter,
+ method: HyperAPIMethod,
+ path: string,
+): Promise {
+ // console.log('[useRouter]', method, path);
+ const url = `file://${path}`;
+ const result = await router.fetch({
+ method,
+ url,
+ });
+
+ if (result) {
+ // console.log('result', result);
+ return result;
+ }
+
+ const result_unknown = await router.fetch({
+ method: 'UNKNOWN',
+ url,
+ });
+
+ return result_unknown ? 'INVALID' : 'NOT_EXISTS';
+}
diff --git a/src/router/file-tree.ts b/src/router/file-tree.ts
new file mode 100644
index 0000000..b3a3ef7
--- /dev/null
+++ b/src/router/file-tree.ts
@@ -0,0 +1,170 @@
+import { readdirSync } from 'node:fs';
+import nodePath from 'node:path';
+import type { HyperAPIMethod } from '../utils/methods.js';
+import { parseFilename } from './filename.js';
+
+const RE_EXT = /\.([cm]?[jt]s)$/i;
+const RE_METHOD = /\.(delete|get|head|options|patch|post|put)$/i;
+
+type WalkState = {
+ route: string | null;
+ specificity: WalkSpecificity;
+};
+enum WalkSpecificityPosition {
+ FILE_METHOD = 0,
+ FILE_ALL = 1,
+ DIRECTORY = 2,
+}
+type WalkSpecificity = {
+ type: number;
+ static_length: number;
+ position: WalkSpecificityPosition;
+};
+type WalkRoute = {
+ specificity: WalkSpecificity;
+ method: Lowercase> | 'all';
+ route: string;
+ path: string;
+};
+type WalkDirectory = {
+ specificity: WalkSpecificity;
+ children: WalkResult;
+};
+export type WalkResult = (WalkRoute | WalkDirectory)[];
+
+/**
+ * Reads the file system and returns a tree of routes sorted correctly.
+ * @param path_given The directory to read.
+ * @param _state Internal state.
+ * @returns -
+ */
+export function readFiles(path_given: string, _state?: WalkState): WalkResult {
+ _state ??= {
+ route: null,
+ specificity: {
+ type: 0,
+ static_length: 0,
+ position: WalkSpecificityPosition.DIRECTORY,
+ },
+ };
+
+ const result_directory: WalkDirectory = {
+ specificity: _state.specificity,
+ children: [],
+ };
+ const result_routes: WalkRoute[] = [];
+
+ const entries = readdirSync(path_given, {
+ withFileTypes: true,
+ });
+ // shuffle response if we are running tests
+ // different filesystems may have different ordering in response
+ if (process.env.NODE_ENV === 'test') {
+ for (let i = entries.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
+ [entries[i], entries[j]] = [entries[j]!, entries[i]!]; // swap elements
+ }
+ }
+
+ for (const entry of entries) {
+ const path = nodePath.join(entry.parentPath, entry.name);
+
+ if (entry.isFile()) {
+ let name = entry.name.replace(RE_EXT, '');
+
+ if (name.endsWith('.test')) {
+ continue;
+ }
+
+ let method: WalkRoute['method'] = 'all';
+ name = name.replace(RE_METHOD, (_, g1) => {
+ method = g1.toLowerCase() as WalkRoute['method'];
+ return '';
+ });
+
+ const { self, route } = parseFilename(name);
+ const specificity_position =
+ method === 'all'
+ ? WalkSpecificityPosition.FILE_ALL
+ : WalkSpecificityPosition.FILE_METHOD;
+
+ if (self) {
+ result_routes.push({
+ specificity: {
+ ..._state.specificity,
+ position: specificity_position,
+ },
+ method,
+ route: _state.route ?? '/',
+ path,
+ });
+ }
+
+ if (route) {
+ result_directory.children.push({
+ specificity: {
+ ...route.specificity,
+ position: specificity_position,
+ },
+ method,
+ route: (_state.route ?? '') + nodePath.sep + route.part,
+ path,
+ });
+ }
+ } else if (entry.isDirectory()) {
+ const { self, route } = parseFilename(entry.name);
+ if (self) {
+ throw new Error(
+ `Invalid directory name "${entry.name}" at "${path}". Can not use optional catch-all in directory name.`,
+ );
+ }
+
+ if (!route) {
+ throw new Error(`Invalid directory name "${entry.name}" at "${path}".`);
+ }
+
+ result_directory.children.push(
+ ...readFiles(path, {
+ route: (_state.route ?? '') + nodePath.sep + route.part,
+ specificity: {
+ ...route.specificity,
+ position: WalkSpecificityPosition.DIRECTORY,
+ },
+ }),
+ );
+ }
+ }
+
+ sortRoutes(result_directory.children);
+
+ const result = [result_directory, ...result_routes];
+ // sort result itself if we are at the root
+ // otherwise, sort will be done by the parent
+ if (_state.route === null) {
+ sortRoutes(result);
+ }
+
+ return result;
+}
+
+/**
+ * Sorts the routes in the given result.
+ * @param result The result to sort.
+ */
+function sortRoutes(result: WalkResult) {
+ result.sort((a, b) => {
+ if (a.specificity.type !== b.specificity.type) {
+ return a.specificity.type - b.specificity.type;
+ }
+
+ if (a.specificity.static_length !== b.specificity.static_length) {
+ return b.specificity.static_length - a.specificity.static_length;
+ }
+
+ if (a.specificity.position !== b.specificity.position) {
+ return a.specificity.position - b.specificity.position;
+ }
+
+ return 0;
+ });
+}
diff --git a/src/router/filename.ts b/src/router/filename.ts
new file mode 100644
index 0000000..b89c101
--- /dev/null
+++ b/src/router/filename.ts
@@ -0,0 +1,106 @@
+const RE_OPTIONAL_CATCH_ALL = /^\[\[\.\.\.([a-z_][\da-z_]*)\]\]$/i;
+const RE_GREEDY = /^\[\.\.\.([a-z_][\da-z_]*)\]$/i;
+
+type ParsedFilename = {
+ self?: boolean;
+ route?: {
+ part: string;
+ specificity: {
+ type: number;
+ static_length: number;
+ };
+ };
+};
+
+/**
+ * Returns specificity for route.
+ *
+ * Values:
+ * - 0: static route (e.g. `/foo`)
+ * - 1: route with parameter (e.g. `/foo-:id`)
+ * - 2: route with optional parameter (e.g. `/foo-:id?`)
+ * - 3: route with greedy parameter (e.g. `/foo-:id+`)
+ * - 4: (NOT USED) route with wildcard (e.g. `/*`)
+ * @param name -
+ * @returns -
+ */
+export function parseFilename(name: string): ParsedFilename {
+ if (name === 'index' || name.length === 0) {
+ return {
+ self: true,
+ };
+ }
+
+ // Optional catch-all uses double brackets [[...slug]]
+ const match_optional_catch_all = RE_OPTIONAL_CATCH_ALL.exec(name);
+ if (match_optional_catch_all) {
+ // We return a sentinel; caller will produce both '/base' and '/base/:slug+'
+ return {
+ self: true,
+ route: {
+ part: `:${match_optional_catch_all[1]}+`,
+ specificity: {
+ type: 3,
+ static_length: 0,
+ },
+ },
+ };
+ }
+
+ // Greedy: [...slug] -> :slug+
+ const match_greedy = RE_GREEDY.exec(name);
+ if (match_greedy) {
+ return {
+ route: {
+ part: `:${match_greedy[1]}+`,
+ specificity: {
+ type: 3,
+ static_length: 0,
+ },
+ },
+ };
+ }
+
+ let has_optional = false;
+ let static_length = name.length;
+ const route_part = name.replaceAll(
+ /(\[([a-z_][\da-z_]*)\]|\[\[([a-z_][\da-z_]*)\]\])([^\da-z_]|$)/gi,
+ (...args) => {
+ static_length -= args[1].length;
+
+ if (args[3] !== undefined) {
+ has_optional = true;
+ return `:${args[3]}?${args[4]}`;
+ }
+
+ return `:${args[2]}${args[4]}`;
+ },
+ );
+
+ // something was replaced
+ if (name !== route_part) {
+ return {
+ route: {
+ part: route_part,
+ specificity: {
+ type: has_optional ? 2 : 1,
+ static_length,
+ },
+ },
+ };
+ }
+
+ if (name.includes('[') !== true) {
+ return {
+ route: {
+ part: route_part,
+ specificity: {
+ type: 0,
+ static_length: 0,
+ },
+ },
+ };
+ }
+
+ throw new Error(`Invalid filename "${name}".`);
+}
diff --git a/src/utils/methods.ts b/src/utils/methods.ts
new file mode 100644
index 0000000..8ebc0fa
--- /dev/null
+++ b/src/utils/methods.ts
@@ -0,0 +1,27 @@
+export type HyperAPIMethod =
+ | 'DELETE'
+ | 'GET'
+ | 'HEAD'
+ | 'OPTIONS'
+ | 'PATCH'
+ | 'POST'
+ | 'PUT'
+ | 'UNKNOWN';
+
+/**
+ * Checks if the given value is a valid HyperAPI method.
+ * @param method The HTTP method to check.
+ * @returns -
+ */
+export function isHyperApiMethod(method: unknown): method is HyperAPIMethod {
+ return (
+ method === 'DELETE'
+ || method === 'GET'
+ || method === 'HEAD'
+ || method === 'OPTIONS'
+ || method === 'PATCH'
+ || method === 'POST'
+ || method === 'PUT'
+ || method === 'UNKNOWN'
+ );
+}
diff --git a/src/utils/record.test.ts b/src/utils/record.test.ts
new file mode 100644
index 0000000..c2e826a
--- /dev/null
+++ b/src/utils/record.test.ts
@@ -0,0 +1,24 @@
+import { expect, test } from 'vitest';
+import { hasCommonKeys, isRecord } from './record.js';
+
+test('isRecord', () => {
+ expect(isRecord({})).toBe(true);
+ expect(isRecord({ a: 1 })).toBe(true);
+ expect(isRecord({ a: 1, b: '2' })).toBe(true);
+ expect(isRecord([1, 2, 3])).toBe(false);
+ expect(isRecord(null)).toBe(false);
+ expect(isRecord(undefined)).toBe(false);
+ expect(isRecord(1)).toBe(false);
+ expect(isRecord('string')).toBe(false);
+ expect(isRecord(true)).toBe(false);
+ expect(isRecord(false)).toBe(false);
+});
+
+test('hasCommonKeys', () => {
+ expect(hasCommonKeys({ a: 1 }, { a: 2 })).toBe(true);
+ expect(hasCommonKeys({ a: 1 }, { b: 2 })).toBe(false);
+ expect(hasCommonKeys({ a: 1 }, { a: 2, b: 3 })).toBe(true);
+ expect(hasCommonKeys({ a: 1 }, { b: 2, c: 3 })).toBe(false);
+ expect(hasCommonKeys({ a: 1 }, { a: 2, b: 3, c: 4 })).toBe(true);
+ expect(hasCommonKeys({ a: 1 }, { b: 2, c: 3, d: 4 })).toBe(false);
+});
diff --git a/src/utils/record.ts b/src/utils/record.ts
new file mode 100644
index 0000000..3cd9f70
--- /dev/null
+++ b/src/utils/record.ts
@@ -0,0 +1,33 @@
+export type BaseRecord = Record;
+export type EmptyObject = Record;
+
+/**
+ * Check if a value is a record.
+ * @param value -
+ * @returns -
+ */
+export function isRecord(value: unknown): value is BaseRecord {
+ return (
+ typeof value === 'object'
+ && value !== null
+ && !Array.isArray(value)
+ && value.constructor === Object
+ && Object.prototype.toString.call(value) === '[object Object]'
+ );
+}
+
+/**
+ * Checks if there are common keys in both object.
+ * @param value1 -
+ * @param value2 -
+ * @returns -
+ */
+export function hasCommonKeys(value1: BaseRecord, value2: BaseRecord): boolean {
+ for (const key of Object.keys(value2)) {
+ if (Object.hasOwn(value1, key)) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/utils/types.test.ts b/src/utils/types.test.ts
new file mode 100644
index 0000000..c6fbe69
--- /dev/null
+++ b/src/utils/types.test.ts
@@ -0,0 +1,38 @@
+import { expectTypeOf, test } from 'vitest';
+import type { EmptyObject } from './record.js';
+import type { Extend } from './types.js';
+
+test('Extend', () => {
+ // type T = Extend<{ a: string }, { b: number }>;
+ expectTypeOf>().toEqualTypeOf<{
+ a: string;
+ b: number;
+ }>();
+
+ expectTypeOf>().toEqualTypeOf<{
+ a: number;
+ }>();
+
+ expectTypeOf>().toEqualTypeOf<{
+ a: string;
+ }>();
+
+ expectTypeOf>().toEqualTypeOf<{
+ a: string;
+ }>();
+
+ // type T = Extend;
+ expectTypeOf>().toEqualTypeOf();
+
+ // type T = Extend<{ a: string }, never>;
+ expectTypeOf>().toEqualTypeOf<{
+ a: string;
+ }>();
+
+ expectTypeOf>().toEqualTypeOf<{
+ a: string;
+ }>();
+
+ // type T = Extend<{ a: string }, void>;
+ expectTypeOf>().toEqualTypeOf<{ a: string }>();
+});
diff --git a/src/utils/types.ts b/src/utils/types.ts
new file mode 100644
index 0000000..aee43a5
--- /dev/null
+++ b/src/utils/types.ts
@@ -0,0 +1,27 @@
+import type { IsEqual, IsNever, Merge } from 'type-fest';
+import type { HyperAPIRequest } from '../request.js';
+import type { BaseRecord, EmptyObject } from './record.js';
+
+type IsRecord = IsNever extends true
+ ? false
+ : T extends void
+ ? false
+ : IsEqual extends true
+ ? false
+ : true;
+
+export type Join<
+ R extends HyperAPIRequest,
+ ReqExtra extends BaseRecord,
+> = IsRecord extends true ? Merge : R;
+
+export type Extend<
+ V1 extends BaseRecord,
+ V2 extends BaseRecord | void,
+> = IsRecord extends true
+ ? IsRecord extends true
+ ? Merge
+ : V1
+ : IsRecord extends true
+ ? Exclude
+ : EmptyObject;
diff --git a/test/driver.ts b/test/driver.ts
new file mode 100644
index 0000000..0f1f518
--- /dev/null
+++ b/test/driver.ts
@@ -0,0 +1,37 @@
+import { HyperAPIDriver } from '../src/driver.js';
+import { HyperAPIError } from '../src/error.js';
+import type { HyperAPIRequest, HyperAPIRequestArgs } from '../src/request.js';
+import type { HyperAPIMethod } from '../src/utils/methods.js';
+import type { EmptyObject } from '../src/utils/record.js';
+
+export interface TestRequest
+ extends HyperAPIRequest {
+ foo: string;
+}
+
+export class HyperAPITestDriver extends HyperAPIDriver {
+ async trigger(
+ method: HyperAPIMethod,
+ path: string,
+ args: Record = {},
+ ): Promise<[boolean, unknown, { status: number | undefined }]> {
+ const response = await this.emitRequest({
+ method,
+ path,
+ args,
+ foo: 'bar',
+ });
+
+ if (response instanceof HyperAPIError) {
+ return [
+ false,
+ response.getResponse(),
+ {
+ status: response.httpStatus,
+ },
+ ];
+ }
+
+ return [true, response, { status: 200 }];
+ }
+}
diff --git a/test/errors.test.ts b/test/errors.test.ts
new file mode 100644
index 0000000..e8d6441
--- /dev/null
+++ b/test/errors.test.ts
@@ -0,0 +1,22 @@
+import { expect, test } from 'vitest';
+import { driver } from './server.js';
+
+test('api', async () => {
+ const [success, response] = await driver.trigger('GET', 'errors/api');
+
+ expect(success).toBe(false);
+ expect(response).toStrictEqual({
+ code: 10,
+ description: 'Endpoint is busy',
+ });
+});
+
+test('internal', async () => {
+ const [success, response] = await driver.trigger('GET', 'errors/internal');
+
+ expect(success).toBe(false);
+ expect(response).toStrictEqual({
+ code: 3,
+ description: 'Internal error',
+ });
+});
diff --git a/test/hyper-api/errors/api.ts b/test/hyper-api/errors/api.ts
new file mode 100644
index 0000000..ebbd738
--- /dev/null
+++ b/test/hyper-api/errors/api.ts
@@ -0,0 +1,6 @@
+import { HyperAPIBusyError } from '../../../src/api-errors.js';
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action(() => {
+ throw new HyperAPIBusyError();
+});
diff --git a/test/hyper-api/errors/internal.ts b/test/hyper-api/errors/internal.ts
new file mode 100644
index 0000000..e861bfe
--- /dev/null
+++ b/test/hyper-api/errors/internal.ts
@@ -0,0 +1,5 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action(() => {
+ throw new Error('This is a test error inside API module.');
+});
diff --git a/test/hyper-api/module/async.ts b/test/hyper-api/module/async.ts
new file mode 100644
index 0000000..6add002
--- /dev/null
+++ b/test/hyper-api/module/async.ts
@@ -0,0 +1,7 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action(async () => {
+ await Promise.resolve();
+
+ return { foo: 1 };
+});
diff --git a/test/hyper-api/module/main.ts b/test/hyper-api/module/main.ts
new file mode 100644
index 0000000..80c8b12
--- /dev/null
+++ b/test/hyper-api/module/main.ts
@@ -0,0 +1,7 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ external: 'external' in request ? request.external : undefined,
+ };
+});
diff --git a/test/hyper-api/module/set.ts b/test/hyper-api/module/set.ts
new file mode 100644
index 0000000..0ea4fee
--- /dev/null
+++ b/test/hyper-api/module/set.ts
@@ -0,0 +1,10 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi
+ .module()
+ .set('extra', () => 1)
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/set/async.ts b/test/hyper-api/module/set/async.ts
new file mode 100644
index 0000000..38a7259
--- /dev/null
+++ b/test/hyper-api/module/set/async.ts
@@ -0,0 +1,13 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .set('extra', async () => {
+ await Promise.resolve();
+ return 1;
+ })
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/set/error.ts b/test/hyper-api/module/set/error.ts
new file mode 100644
index 0000000..410eaa7
--- /dev/null
+++ b/test/hyper-api/module/set/error.ts
@@ -0,0 +1,13 @@
+import { HyperAPIInvalidParametersError } from '../../../../src/api-errors.js';
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .set('extra', () => {
+ throw new HyperAPIInvalidParametersError();
+ })
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/set/multiple.ts b/test/hyper-api/module/set/multiple.ts
new file mode 100644
index 0000000..f0ef688
--- /dev/null
+++ b/test/hyper-api/module/set/multiple.ts
@@ -0,0 +1,11 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .set('extra1', () => 1 as const)
+ .set('extra2', () => 2 as const)
+ .action((request) => {
+ return {
+ extra: [request.extra1, request.extra2],
+ };
+ });
diff --git a/test/hyper-api/module/set/override.ts b/test/hyper-api/module/set/override.ts
new file mode 100644
index 0000000..881459a
--- /dev/null
+++ b/test/hyper-api/module/set/override.ts
@@ -0,0 +1,11 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .set('extra', () => 1)
+ .set('extra', 2 as const)
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/use.ts b/test/hyper-api/module/use.ts
new file mode 100644
index 0000000..63aef32
--- /dev/null
+++ b/test/hyper-api/module/use.ts
@@ -0,0 +1,12 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi
+ .module()
+ .use(() => {
+ return { extra: 1 };
+ })
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/use/async.ts b/test/hyper-api/module/use/async.ts
new file mode 100644
index 0000000..24ee3b6
--- /dev/null
+++ b/test/hyper-api/module/use/async.ts
@@ -0,0 +1,15 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .use(async () => {
+ await Promise.resolve();
+ return {
+ extra: 1,
+ };
+ })
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/use/error.ts b/test/hyper-api/module/use/error.ts
new file mode 100644
index 0000000..797e5cf
--- /dev/null
+++ b/test/hyper-api/module/use/error.ts
@@ -0,0 +1,13 @@
+import { HyperAPIInvalidParametersError } from '../../../../src/api-errors.js';
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .use(() => {
+ throw new HyperAPIInvalidParametersError();
+ })
+ .action((_request) => {
+ return {
+ foo: 'request is never',
+ };
+ });
diff --git a/test/hyper-api/module/use/multiple.ts b/test/hyper-api/module/use/multiple.ts
new file mode 100644
index 0000000..9800771
--- /dev/null
+++ b/test/hyper-api/module/use/multiple.ts
@@ -0,0 +1,16 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .use(() => {
+ return { extra1: 1 };
+ })
+ .use(async () => {
+ await Promise.resolve();
+ return { extra2: 2 };
+ })
+ .action((request) => {
+ return {
+ extra: [request.extra1, request.extra2],
+ };
+ });
diff --git a/test/hyper-api/module/use/override.ts b/test/hyper-api/module/use/override.ts
new file mode 100644
index 0000000..1c80958
--- /dev/null
+++ b/test/hyper-api/module/use/override.ts
@@ -0,0 +1,15 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi
+ .module()
+ .use(() => {
+ return { extra: 1 };
+ })
+ .use(() => {
+ return { extra: 2 as const };
+ })
+ .action((request) => {
+ return {
+ extra: request.extra,
+ };
+ });
diff --git a/test/hyper-api/module/void.ts b/test/hyper-api/module/void.ts
new file mode 100644
index 0000000..e7a2f92
--- /dev/null
+++ b/test/hyper-api/module/void.ts
@@ -0,0 +1,5 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action(() => {
+ process.hrtime();
+});
diff --git a/test/hyper-api/router/all.ts b/test/hyper-api/router/all.ts
new file mode 100644
index 0000000..e305655
--- /dev/null
+++ b/test/hyper-api/router/all.ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/catch/[...slug].ts b/test/hyper-api/router/catch/[...slug].ts
new file mode 100644
index 0000000..a044295
--- /dev/null
+++ b/test/hyper-api/router/catch/[...slug].ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/index.get.ts b/test/hyper-api/router/index.get.ts
new file mode 100644
index 0000000..e305655
--- /dev/null
+++ b/test/hyper-api/router/index.get.ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/optional-catch/[[...slug]].ts b/test/hyper-api/router/optional-catch/[[...slug]].ts
new file mode 100644
index 0000000..a044295
--- /dev/null
+++ b/test/hyper-api/router/optional-catch/[[...slug]].ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/optional-slug/[name].[[ext]].ts b/test/hyper-api/router/optional-slug/[name].[[ext]].ts
new file mode 100644
index 0000000..a044295
--- /dev/null
+++ b/test/hyper-api/router/optional-slug/[name].[[ext]].ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/slug/[slug].ts b/test/hyper-api/router/slug/[slug].ts
new file mode 100644
index 0000000..a044295
--- /dev/null
+++ b/test/hyper-api/router/slug/[slug].ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/slug/[slug]/x.ts b/test/hyper-api/router/slug/[slug]/x.ts
new file mode 100644
index 0000000..7223846
--- /dev/null
+++ b/test/hyper-api/router/slug/[slug]/x.ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/slug/x-[slug]-x.ts b/test/hyper-api/router/slug/x-[slug]-x.ts
new file mode 100644
index 0000000..a044295
--- /dev/null
+++ b/test/hyper-api/router/slug/x-[slug]-x.ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/slug/x-[slug].ts b/test/hyper-api/router/slug/x-[slug].ts
new file mode 100644
index 0000000..a044295
--- /dev/null
+++ b/test/hyper-api/router/slug/x-[slug].ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/hyper-api/router/x.get.ts b/test/hyper-api/router/x.get.ts
new file mode 100644
index 0000000..e305655
--- /dev/null
+++ b/test/hyper-api/router/x.get.ts
@@ -0,0 +1,9 @@
+import { hyperApi } from '../../server.js';
+
+export default hyperApi.module().action((request) => {
+ return {
+ path: decodeURIComponent(import.meta.url.split('/hyper-api/')[1]!),
+ method: request.method,
+ args: request.args,
+ };
+});
diff --git a/test/module.test.ts b/test/module.test.ts
new file mode 100644
index 0000000..ce79bd8
--- /dev/null
+++ b/test/module.test.ts
@@ -0,0 +1,90 @@
+import { describe, expect, test } from 'vitest';
+import { driver } from './server.js';
+
+describe('action', () => {
+ test('async', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/async');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ foo: 1 });
+ });
+
+ test('returns void', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/void');
+ expect(status).toBe(true);
+ expect(data).toBeUndefined();
+ });
+});
+
+describe('use', () => {
+ test('single', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/use');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ extra: 1 });
+ });
+
+ test('async', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/use/async');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ extra: 1 });
+ });
+
+ test('multiple', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/use/multiple');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({
+ extra: [1, 2],
+ });
+ });
+
+ test('override', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/use/override');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ extra: 2 });
+ });
+
+ test('error', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/use/error');
+ expect(status).toBe(false);
+ expect(data).toStrictEqual({
+ code: 2,
+ description: 'One of the parameters specified was missing or invalid',
+ });
+ });
+});
+
+describe('set', () => {
+ test('single', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/set');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ extra: 1 });
+ });
+
+ test('async', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/set/async');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ extra: 1 });
+ });
+
+ test('multiple', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/set/multiple');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({
+ extra: [1, 2],
+ });
+ });
+
+ test('override', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/set/override');
+ expect(status).toBe(true);
+ expect(data).toStrictEqual({ extra: 2 });
+ });
+
+ test('error', async () => {
+ const [status, data] = await driver.trigger('GET', 'module/set/error');
+ expect(status).toBe(false);
+ expect(data).toStrictEqual({
+ code: 2,
+ description: 'One of the parameters specified was missing or invalid',
+ });
+ });
+});
diff --git a/test/router.test.ts b/test/router.test.ts
new file mode 100644
index 0000000..03fd8c3
--- /dev/null
+++ b/test/router.test.ts
@@ -0,0 +1,248 @@
+import { describe, expect, test } from 'vitest';
+import { HyperAPI } from '../src/main.js';
+import { HyperAPITestDriver } from './driver.js';
+import { driver } from './server.js';
+
+// eslint-disable-next-line jsdoc/require-jsdoc
+function repeat(times: number): number[] {
+ return Array.from({ length: times }, (_, i) => i);
+}
+
+describe('ALL', () => {
+ test('with GET', async () => {
+ const [success, response] = await driver.trigger('GET', 'router/all');
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/all.ts',
+ method: 'GET',
+ args: {},
+ });
+ });
+
+ test('with POST', async () => {
+ const [success, response] = await driver.trigger('POST', 'router/all');
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/all.ts',
+ method: 'POST',
+ args: {},
+ });
+ });
+});
+
+describe('only GET', () => {
+ test('with GET', async () => {
+ const [success, response] = await driver.trigger('GET', 'router/x');
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/x.get.ts',
+ method: 'GET',
+ args: {},
+ });
+ });
+
+ // test('with POST', async () => {
+ // const [success, response, http] = await driver.trigger(
+ // 'POST',
+ // 'router/x',
+ // );
+ // expect(success).toBe(false);
+ // expect(response).toStrictEqual({
+ // code: 5,
+ // description: 'Unknown method called',
+ // });
+ // expect(http.status).toBe(405);
+ // });
+});
+
+test('only method in the filename', async () => {
+ const [success, response] = await driver.trigger('GET', 'router');
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/index.get.ts',
+ method: 'GET',
+ args: {},
+ });
+});
+
+// rerun tests shuffling files simulating different order of files returned by filesystem
+// our router should order files by their name (static, slugs, optional)
+describe.each(repeat(10))('slug', () => {
+ const driver_slug = new HyperAPITestDriver();
+ const _ = new HyperAPI(
+ driver_slug,
+ new URL('hyper-api', import.meta.url).pathname,
+ );
+
+ test('filename with slug in the end', async () => {
+ const [success, response] = await driver_slug.trigger(
+ 'GET',
+ 'router/slug/x-123',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/slug/x-[slug].ts',
+ method: 'GET',
+ args: { slug: '123' },
+ });
+ });
+
+ test('filename with slug in the middle', async () => {
+ const [success, response] = await driver_slug.trigger(
+ 'GET',
+ 'router/slug/x-123-x',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/slug/x-[slug]-x.ts',
+ method: 'GET',
+ args: { slug: '123' },
+ });
+ });
+
+ test('filename with slug only', async () => {
+ const [success, response] = await driver_slug.trigger(
+ 'GET',
+ 'router/slug/123',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/slug/[slug].ts',
+ method: 'GET',
+ args: { slug: '123' },
+ });
+ });
+
+ test('directory with slug only', async () => {
+ const [success, response] = await driver_slug.trigger(
+ 'GET',
+ 'router/slug/123/x',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/slug/[slug]/x.ts',
+ method: 'GET',
+ args: { slug: '123' },
+ });
+ });
+});
+
+describe('optional slug', () => {
+ describe('filename with slug and optional slug', () => {
+ test('pass optional', async () => {
+ const [success, response] = await driver.trigger(
+ 'GET',
+ 'router/optional-slug/image.png',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/optional-slug/[name].[[ext]].ts',
+ method: 'GET',
+ args: { name: 'image', ext: 'png' },
+ });
+ });
+
+ test('omit optional', async () => {
+ const [success, response] = await driver.trigger(
+ 'GET',
+ 'router/optional-slug/image',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/optional-slug/[name].[[ext]].ts',
+ method: 'GET',
+ args: { name: 'image', ext: undefined },
+ });
+ });
+ });
+});
+
+describe('catch all', () => {
+ test('0 level deep', async () => {
+ const [success, response] = await driver.trigger('GET', 'router/catch');
+ expect(success).toBe(false);
+ expect(response).toStrictEqual({
+ code: 5,
+ description: 'Unknown method called',
+ });
+ });
+
+ test('1 level deep', async () => {
+ const [success, response] = await driver.trigger('GET', 'router/catch/foo');
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/catch/[...slug].ts',
+ method: 'GET',
+ args: { slug: 'foo' },
+ });
+ });
+
+ test('3 level deep', async () => {
+ const [success, response] = await driver.trigger(
+ 'GET',
+ 'router/catch/foo/bar/baz',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/catch/[...slug].ts',
+ method: 'GET',
+ args: { slug: 'foo/bar/baz' },
+ });
+ });
+});
+
+describe('optional catch all', () => {
+ test('0 level deep', async () => {
+ const [success, response] = await driver.trigger(
+ 'GET',
+ 'router/optional-catch',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/optional-catch/[[...slug]].ts',
+ method: 'GET',
+ args: {},
+ });
+ });
+
+ test('1 level deep', async () => {
+ const [success, response] = await driver.trigger(
+ 'GET',
+ 'router/optional-catch/foo',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/optional-catch/[[...slug]].ts',
+ method: 'GET',
+ args: { slug: 'foo' },
+ });
+ });
+
+ test('3 level deep', async () => {
+ const [success, response] = await driver.trigger(
+ 'GET',
+ 'router/optional-catch/foo/bar/baz',
+ );
+ expect(success).toBe(true);
+ expect(response).toStrictEqual({
+ path: 'router/optional-catch/[[...slug]].ts',
+ method: 'GET',
+ args: { slug: 'foo/bar/baz' },
+ });
+ });
+});
+
+describe('errors', () => {
+ test('unknown method', async () => {
+ const [success, response, http] = await driver.trigger(
+ 'POST',
+ 'router/unknown',
+ );
+ expect(success).toBe(false);
+ expect(response).toStrictEqual({
+ code: 5,
+ description: 'Unknown method called',
+ });
+ expect(http.status).toBe(404);
+ });
+});
diff --git a/test/server.ts b/test/server.ts
new file mode 100644
index 0000000..7ca4a63
--- /dev/null
+++ b/test/server.ts
@@ -0,0 +1,8 @@
+import { HyperAPI } from '../src/main.js';
+import { HyperAPITestDriver } from './driver.js';
+
+export const driver = new HyperAPITestDriver();
+export const hyperApi = new HyperAPI(
+ driver,
+ new URL('hyper-api', import.meta.url).pathname,
+);
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 0000000..e666180
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "target": "es2022",
+ "moduleDetection": "force",
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+ "module": "preserve",
+ "noEmit": true,
+ "lib": ["es2022"],
+ "declaration": true,
+ "isolatedDeclarations": false,
+ "tsBuildInfoFile": "/dev/null"
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..4e14791
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.main.json" },
+ { "path": "./tsconfig.test.json" }
+ ]
+}
diff --git a/tsconfig.main.json b/tsconfig.main.json
new file mode 100644
index 0000000..256535f
--- /dev/null
+++ b/tsconfig.main.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.base.json",
+ "compilerOptions": {
+ "isolatedDeclarations": true
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": ["src/**/*.test.ts"]
+}
diff --git a/tsconfig.test.json b/tsconfig.test.json
new file mode 100644
index 0000000..4d33b90
--- /dev/null
+++ b/tsconfig.test.json
@@ -0,0 +1,4 @@
+{
+ "extends": "./tsconfig.base.json",
+ "include": ["src/**/*.test.ts", "test/**/*.ts"]
+}