diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..8249554 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "rules": { + "prettier/prettier": "error" + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3473f9c..0ceac30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,38 +1,61 @@ -name: CLI Completion Tests - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - test: - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4.0.0 - with: - version: 8 - - - name: Set node version to 20 - uses: actions/setup-node@v4 - with: - node-version: 20 - registry-url: https://registry.npmjs.org/ - cache: "pnpm" - - - name: Install deps - run: pnpm install - - - name: Run tests - run: pnpm test +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + name: Tests + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.0.0 + + - name: Set node version to 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + cache: 'pnpm' + + - name: Install deps + run: pnpm install + + - name: Run tests + run: pnpm test + + typecheck: + name: Type Check + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.0.0 + + - name: Set node version to 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + cache: pnpm + + - name: Install deps + run: pnpm install + + - name: Type-check + run: pnpm type-check diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..606b1e3 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,27 @@ +name: Prettier Check + +on: [push, pull_request] + +jobs: + prettier: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.0.0 + + - name: Set node version to 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + cache: pnpm + + - name: Install deps + run: pnpm install + + - name: Run Prettier Check + run: npm run format:check diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 38a1b76..32bce5d 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -1,27 +1,25 @@ -name: pkg.pr.new -on: [push, pull_request] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4.0.0 - with: - version: 8 - - - name: Set node version to 20 - uses: actions/setup-node@v4 - with: - node-version: 20 - registry-url: https://registry.npmjs.org/ - cache: "pnpm" - - - name: Install deps - run: pnpm install - - - run: pnpx pkg-pr-new publish +name: pkg.pr.new +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.0.0 + + - name: Set node version to 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + cache: 'pnpm' + + - name: Install deps + run: pnpm install + + - run: pnpx pkg-pr-new publish diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..16b0a03 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +node_modules +dist +build +pnpm-lock.yaml \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6bdf86a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2 +} diff --git a/README.md b/README.md index b1a3480..8b1029c 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,187 @@ # tab -- [x] zsh test in git +Shell autocompletions are largely missing in the javascript cli ecosystem. This tool is an attempt to make autocompletions come out of the box for any cli tool. -```zsh -source <(pnpm tsx demo.ts complete zsh) +Tools like git and their autocompletion experience inspired us to build this tool and make the same ability available for any javascript cli project. Developers love hitting the tab key, hence why they prefer tabs over spaces. + +```ts +import { Completion, script } from '@bombsh/tab'; + +const name = 'my-cli'; +const completion = new Completion(); -vite # rest of the completions +completion.addCommand( + 'start', + 'Start the application', + async (previousArgs, toComplete, endsWithSpace) => { + // suggestions + return [ + { value: 'dev', description: 'Start in development mode' }, + { value: 'prod', description: 'Start in production mode' }, + ]; + } +); -pnpm tsx demo.ts complete -- --po +completion.addOption( + 'start', + '--port', + 'Specify the port number', + async (previousArgs, toComplete, endsWithSpace) => { + return [ + { value: '3000', description: 'Development port' }, + { value: '8080', description: 'Production port' }, + ]; + } +); + +// a way of getting the executable path to pass to the shell autocompletion script +function quoteIfNeeded(path: string) { + return path.includes(' ') ? `'${path}'` : path; +} +const execPath = process.execPath; +const processArgs = process.argv.slice(1); +const quotedExecPath = quoteIfNeeded(execPath); +const quotedProcessArgs = processArgs.map(quoteIfNeeded); +const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded); +const x = `${quotedExecPath} ${quotedProcessExecArgs.join(' ')} ${quotedProcessArgs[0]}`; + +if (process.argv[2] === '--') { + // autocompletion logic + await completion.parse(process.argv.slice(2), 'start'); // TODO: remove "start" +} else { + // process.argv[2] can be "zsh", "bash", "fish", "powershell" + script(process.argv[2], name, x); +} ``` -- [x] tests vitest (this should mostly test the completions array, e.g. logs) -- [x] powershell completions generation -- [x] citty support `@bomsh/tab/citty` -- [] `@bombsh/tab` +Now your user can run `source <(my-cli complete zsh)` and they will get completions for the `my-cli` command using the [autocompletion server](#autocompletion-server). -- [] fish -- [] bash +## Adapters + +Since we are heavy users of tools like `cac` and `citty`, we have created adapters for both of them. Ideally, tab would be integrated internally into these tools, but for now, this is a good compromise. + +### `@bombsh/tab/cac` ```ts -const completion = new Completion() -completion.addCommand() -completion.addOption() +import cac from 'cac'; +import tab from '@bombsh/tab/cac'; + +const cli = cac('my-cli'); + +cli.command('dev', 'Start dev server').option('--port ', 'Specify port'); + +const completion = tab(cli); -// better name -completion.parse() +// Get the dev command completion handler +const devCommandCompletion = completion.commands.get('dev'); + +// Get and configure the port option completion handler +const portOptionCompletion = devCommandCompletion.options.get('--port'); +portOptionCompletion.handler = async ( + previousArgs, + toComplete, + endsWithSpace +) => { + return [ + { value: '3000', description: 'Development port' }, + { value: '8080', description: 'Production port' }, + ]; +}; + +cli.parse(); ``` + +Now autocompletion will be available for any specified command and option in your cac instance. If your user writes `my-cli dev --po`, they will get suggestions for the `--port` option. Or if they write `my-cli d` they will get suggestions for the `dev` command. + +Suggestions are missing in the adapters since yet cac or citty do not have a way to provide suggestions (tab just came out!), we'd have to provide them manually. Mutations do not hurt in this situation. + +### `@bombsh/tab/citty` + +```ts +import citty, { defineCommand, createMain } from 'citty'; +import tab from '@bombsh/tab/citty'; + +const main = defineCommand({ + meta: { + name: 'my-cli', + description: 'My CLI tool', + }, +}); + +const devCommand = defineCommand({ + meta: { + name: 'dev', + description: 'Start dev server', + }, + args: { + port: { type: 'string', description: 'Specify port' }, + }, +}); + +main.subCommands = { + dev: devCommand, +}; + +const completion = await tab(main); + +// TODO: addHandler function to export +const devCommandCompletion = completion.commands.get('dev'); + +const portOptionCompletion = devCommandCompletion.options.get('--port'); + +portOptionCompletion.handler = async ( + previousArgs, + toComplete, + endsWithSpace +) => { + return [ + { value: '3000', description: 'Development port' }, + { value: '8080', description: 'Production port' }, + ]; +}; + +const cli = createMain(main); +cli(); +``` + +## Recipe + +`source <(my-cli complete zsh)` won't be enough since the user would have to run this command each time they spin up a new shell instance. + +We suggest this approach for the end user that you as a maintainer might want to push. + +``` +my-cli completion zsh > ~/completion-for-my-cli.zsh +echo 'source ~/completion-for-my-cli.zsh' >> ~/.zshrc +``` + +## Autocompletion Server + +By integrating tab into your cli, your cli would have a new command called `complete`. This is where all the magic happens. And the shell would contact this command to get completions. That's why we call it the autocompletion server. + +```zsh +my-cli complete -- --po +--port Specify the port number +:0 +``` + +The autocompletion server can be a standard to identify whether a package provides autocompletions. Whether running `tool complete --` would result in an output that ends with `:{Number}` (matching the pattern `/:\d+$/`). + +In situations like `my-cli dev --po` you'd have autocompletions! But in the case of `pnpm my-cli dev --po` which is what most of us use, tab does not inject autocompletions for a tool like pnpm. + +Since pnpm already has its own autocompletion [script](https://pnpm.io/completion), this provides the opportunity to check whether a package provides autocompletions and use those autocompletions if available. + +This would also have users avoid injecting autocompletions in their shell config for any tool that provides its own autocompletion script, since pnpm would already support proxying the autocompletions out of the box. + +Other package managers like `npm` and `yarn` can decide whether to support this or not too for more universal support. + +## Inspiration + +- git +- [cobra](https://github.com/spf13/cobra/blob/main/shell_completions.go), without cobra, tab would have took 10x longer to build + +## TODO + +- [] fish +- [] bash diff --git a/bash.ts b/bash.ts deleted file mode 100644 index 3f6a4f1..0000000 --- a/bash.ts +++ /dev/null @@ -1,2 +0,0 @@ -export function generate(name: string, exec: string) { -} diff --git a/cac.ts b/cac.ts deleted file mode 100644 index 6050b75..0000000 --- a/cac.ts +++ /dev/null @@ -1,304 +0,0 @@ -// @bombsh/tab/cac -import { CAC } from "cac"; -import * as zsh from "./zsh"; -import * as bash from "./bash"; -import * as fish from "./fish"; -import * as powershell from "./powershell"; -import { - flagMap, - Positional, - positionalMap, - ShellCompDirective, -} from "./shared"; - -function quoteIfNeeded(path: string): string { - return path.includes(" ") ? `'${path}'` : path; -} - -const execPath = process.execPath; -const processArgs = process.argv.slice(1); - -// Apply the quoting function to each part of x -// This ensures that paths like "Program Files" are quoted for PowerShell execution. -const quotedExecPath = quoteIfNeeded(execPath); -const quotedProcessArgs = processArgs.map(quoteIfNeeded); -const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded); - -const x = `${quotedExecPath} ${quotedProcessExecArgs.join(" ")} ${ - quotedProcessArgs[0] -}`; - -export default function tab(instance: CAC): void { - instance.command("complete [shell]").action(async (shell, extra) => { - switch (shell) { - case "zsh": { - const script = zsh.generate(instance.name, x); - console.log(script); - break; - } - case "bash": { - const script = bash.generate(instance.name, x); - console.log(script); - break; - } - case "fish": { - const script = fish.generate(instance.name, x); - console.log(script); - break; - } - case "powershell": { - const script = powershell.generate(instance.name, x); - console.log(script); - break; - } - default: { - const args: string[] = extra["--"]; - - instance.showHelpOnExit = false; - let directive = ShellCompDirective.ShellCompDirectiveDefault; - - const endsWithSpace = args[args.length - 1] === ""; - if (endsWithSpace) { - args.pop(); - } - - let toComplete = args[args.length - 1] || ""; - const previousArgs = args.slice(0, -1); - - const completions: string[] = []; - - instance.unsetMatchedCommand(); - instance.parse([execPath, processArgs[0], ...previousArgs], { - run: false, - }); - - const command = instance.matchedCommand ?? instance.globalCommand; - - const options = [ - ...new Set([ - ...(command?.options ?? []), - ...instance.globalCommand.options, - ]), - ]; - - let isCompletingFlagValue = false; - let flagName = ""; - let option: (typeof options)[number] | null = null; - const lastArg = previousArgs[previousArgs.length - 1]; - - function processOption() { - const matchedOption = options.find((o) => - o.names.some((name) => name === flagName) - ); - - if (matchedOption && !matchedOption.isBoolean) { - isCompletingFlagValue = true; - option = matchedOption; - if (endsWithSpace) { - toComplete = ""; - } - } else { - isCompletingFlagValue = false; - option = null; - } - } - - if (toComplete.startsWith("--")) { - // Long option - flagName = toComplete.slice(2); - const equalsIndex = flagName.indexOf("="); - if (equalsIndex !== -1 && !endsWithSpace) { - // Option with '=', get the name before '=' - flagName = flagName.slice(0, equalsIndex); - toComplete = toComplete.slice(toComplete.indexOf("=") + 1); - processOption(); - } else if (!endsWithSpace) { - // If not ending with space, still typing option name - flagName = ""; - } else { - // User pressed space after typing the option name - processOption(); - toComplete = ""; - } - } else if (toComplete.startsWith("-") && toComplete.length > 1) { - // Short option - flagName = toComplete.slice(1); - if (!endsWithSpace) { - // Still typing option name - flagName = ""; - } else { - processOption(); - toComplete = ""; - } - } else if (lastArg?.startsWith("--") && !endsWithSpace) { - flagName = lastArg.slice(2); - processOption(); - } else if ( - lastArg?.startsWith("-") && - lastArg.length > 1 && - !endsWithSpace - ) { - flagName = lastArg.slice(2); - processOption(); - } - - if (isCompletingFlagValue) { - const flagCompletionFn = flagMap.get( - `${command.name} ${option?.name}` - ); - - if (flagCompletionFn) { - // Call custom completion function for the flag - const comps = await flagCompletionFn(previousArgs, toComplete); - completions.push( - ...comps.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - directive = ShellCompDirective.ShellCompDirectiveNoFileComp; - } else { - // Default completion (e.g., file completion) - directive = ShellCompDirective.ShellCompDirectiveDefault; - } - } else if (toComplete.startsWith("-") && !endsWithSpace) { - const flag = toComplete.replace(/^-+/, ""); // Remove leading '-' - - // Determine options to suggest - let optionsToSuggest = options.filter((o) => { - const equalToDefault = - "default" in o.config && - instance.options[o.name] === o.config.default; - return ( - o.names.some((name) => name.startsWith(flag)) && - !(instance.options[o.name] && !equalToDefault) - ); - }); - - const requiredOptions = optionsToSuggest.filter((o) => o.required); - - if (requiredOptions.length) { - // Required options not yet specified - optionsToSuggest = requiredOptions; - } - - if (optionsToSuggest.length > 0) { - completions.push( - ...optionsToSuggest.map( - (o) => `--${o.name}\t${o.description ?? ""}` - ) - ); - } - - directive = ShellCompDirective.ShellCompDirectiveNoFileComp; - } else { - instance.parse( - [execPath, processArgs[0], ...previousArgs, toComplete], - { - run: false, - } - ); - const fullCommandName = args - .filter((arg) => !arg.startsWith("-")) - .join(" "); - - for (const c of instance.commands) { - if (c.name === "complete") { - // avoid showing completions for the completion server - continue; - } - const fullCommandParts = fullCommandName.split(" "); - const commandParts: { type: "command"; value: string }[] = c.name - .split(" ") - .map((part) => ({ type: "command", value: part })); - const args: { - type: "positional"; - position: number; - value: Positional; - }[] = - positionalMap.get(c.name)?.map((arg, i) => ({ - type: "positional", - position: i, - value: arg, - })) ?? []; - const parts = [...commandParts, ...args]; - - for (let i = 0; i < parts.length; i++) { - const fullCommandPart = fullCommandParts[i]; - const part = parts[i]; - - if (part.type === "command") { - // Command part matching - if (part.value === fullCommandPart) { - // Command part matches user input, continue to next part - continue; - } else if ( - !fullCommandPart || - part.value.startsWith(fullCommandPart) - ) { - // User is typing this command part, provide completion - completions.push(`${part.value}\t${c.description ?? ""}`); - } - // Command part does not match, break - break; - } else if (part.type === "positional") { - const positional = part.value; - // Positional argument handling - if (part.value.variadic) { - const comps = await positional.completion( - previousArgs, - toComplete - ); - completions.push( - ...comps.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - break; - } - if (typeof fullCommandPart !== "undefined") { - // User has provided input for this positional argument - if (i === fullCommandParts.length - 1 && !endsWithSpace) { - // User is still typing this positional argument, provide completions - const comps = await positional.completion( - previousArgs, - toComplete - ); - completions.push( - ...comps.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - break; - } else { - // Positional argument is already provided, move to next - previousArgs.push(fullCommandPart); - continue; - } - } else { - // User has not provided input for this positional argument - const comps = await positional.completion( - previousArgs, - toComplete - ); - completions.push( - ...comps.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - break; - } - } - } - } - } - - // Output completions - for (const comp of completions) { - console.log(comp.split("\n")[0].trim()); - } - console.log(`:${directive}`); - console.error(`Completion ended with directive: ${directive}`); - } - } - }); -} diff --git a/citty.ts b/citty.ts deleted file mode 100644 index 4219475..0000000 --- a/citty.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { defineCommand } from "citty"; -import * as zsh from "./zsh"; -import * as bash from "./bash"; -import * as fish from "./fish"; -import * as powershell from "./powershell"; -import { - flagMap, - Positional, - positionalMap, - ShellCompDirective, -} from "./shared"; - -function quoteIfNeeded(path) { - return path.includes(" ") ? `'${path}'` : path; -} - -const execPath = process.execPath; -const processArgs = process.argv.slice(1); -const quotedExecPath = quoteIfNeeded(execPath); -const quotedProcessArgs = processArgs.map(quoteIfNeeded); -const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded); -const x = `${quotedExecPath} ${quotedProcessExecArgs.join(" ")} ${quotedProcessArgs[0]}`; - -export default function tab(mainCommand) { - mainCommand.subCommands["complete"] = defineCommand({ - meta: { - name: "complete", - description: "Generate shell completion scripts", - }, - args: { - shell: { - type: "positional", - required: false, - description: "Specify shell type", - }, - }, - async run(ctx) { - let shell: string | undefined = ctx.args.shell; - if (shell?.startsWith("--")) { - shell = undefined; - } - - const extra = ctx.args._ || []; - - switch (shell) { - case "zsh": { - const script = zsh.generate(mainCommand.meta.name, x); - console.log(script); - break; - } - case "bash": { - const script = bash.generate(mainCommand.meta.name, x); - console.log(script); - break; - } - case "fish": { - const script = fish.generate(mainCommand.meta.name, x); - console.log(script); - break; - } - case "powershell": { - const script = powershell.generate(mainCommand.meta.name, x); - console.log(script); - break; - } - default: { - const args = extra; - let directive = ShellCompDirective.ShellCompDirectiveDefault; - const completions: string[] = []; - const endsWithSpace = args[args.length - 1] === ""; - - if (endsWithSpace) args.pop(); - let toComplete = args[args.length - 1] || ""; - const previousArgs = args.slice(0, -1); - - let matchedCommand = mainCommand; - - if (previousArgs.length > 0) { - const lastPrevArg = previousArgs[previousArgs.length - 1]; - if (lastPrevArg.startsWith("--")) { - const flagCompletion = flagMap.get(lastPrevArg); - if (flagCompletion) { - const flagSuggestions = await flagCompletion(previousArgs, toComplete); - completions.push( - ...flagSuggestions.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - completions.forEach((comp) => console.log(comp)); - console.log(`:${directive}`); - return; - } - } - } - - if (toComplete.startsWith("--")) { - if (toComplete === "--") { - const allFlags = [...flagMap.keys()]; - const specifiedFlags = previousArgs.filter(arg => arg.startsWith('--')); - const availableFlags = allFlags.filter(flag => !specifiedFlags.includes(flag)); - - completions.push( - ...availableFlags.map( - (flag) => - `${flag}\t${matchedCommand.args[flag.slice(2)]?.description ?? "Option"}` - ) - ); - } else { - const flagNamePartial = toComplete.slice(2); - const flagKeyPartial = `--${flagNamePartial}`; - - if (flagMap.has(toComplete)) { - const flagCompletion = flagMap.get(toComplete); - if (flagCompletion) { - const flagSuggestions = await flagCompletion(previousArgs, ""); - completions.push( - ...flagSuggestions.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - } - } else { - const matchingFlags = [...flagMap.keys()].filter((flag) => - flag.startsWith(flagKeyPartial) - ); - - completions.push( - ...matchingFlags.map( - (flag) => - `${flag}\t${matchedCommand.args[flag.slice(2)]?.description ?? "Option"}` - ) - ); - } - } - - completions.forEach((comp) => console.log(comp)); - console.log(`:${directive}`); - return; - } - - if (previousArgs.length === 0) { - completions.push( - ...Object.keys(mainCommand.subCommands || {}) - .filter((cmd) => cmd !== "complete") - .map( - (cmd) => - `${cmd}\t${mainCommand.subCommands[cmd]?.meta.description ?? ""}` - ) - ); - } else { - const positionalCompletions = - positionalMap.get(matchedCommand.meta.name) || []; - for (const positional of positionalCompletions) { - const suggestions = await positional.completion( - previousArgs, - toComplete - ); - completions.push( - ...suggestions.map( - (comp) => `${comp.action}\t${comp.description ?? ""}` - ) - ); - } - } - - completions.forEach((comp) => console.log(comp)); - console.log(`:${directive}`); - } - } - }, - }); -} diff --git a/demo.cac.ts b/demo.cac.ts index fb68cd1..e139423 100644 --- a/demo.cac.ts +++ b/demo.cac.ts @@ -1,143 +1,83 @@ -import fs from "fs/promises"; import cac from "cac"; -import { - Callback, - Completion, - flagMap, - Positional, - positionalMap, -} from "./shared"; -import path from "path"; -import tab from "./cac"; +import tab from "./src/cac"; -const cli = cac("vite"); // Using 'vite' as the CLI tool name +const cli = cac("vite"); -// Custom converters (placeholders) -function convertBase(value) { - return value; -} +cli + .option("-c, --config ", `Use specified config file`) + .option("-m, --mode ", `Set env mode`) + .option("-l, --logLevel ", `info | warn | error | silent`); -function convertHost(value) { - return value; -} +cli + .command("dev", "Start dev server") + .option("--host [host]", `Specify hostname`) + .option("--port ", `Specify port`) + .action((options) => {}); -// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/cli.ts -// Global options cli - .option("-c, --config ", `[string] use specified config file`) - .option("--base ", `[string] public base path (default: /)`, { - type: [convertBase], - }) - .option("-l, --logLevel ", `[string] info | warn | error | silent`) - .option("--clearScreen", `[boolean] allow/disable clear screen when logging`) - .option("-d, --debug [feat]", `[string | boolean] show debug logs`) - .option("-f, --filter ", `[string] filter debug logs`) - .option("-m, --mode ", `[string] set env mode`); + .command("dev build", "Build project") + .action((options) => {}); -// Dev command cli - .command("[root]", "start dev server") // default command - .alias("serve") // the command is called 'serve' in Vite's API - .alias("dev") // alias to align with the script name - .option("--host [host]", `[string] specify hostname`, { type: [convertHost] }) - .option("--port ", `[number] specify port`) - .option("--open [path]", `[boolean | string] open browser on startup`) - .option("--cors", `[boolean] enable CORS`) - .option("--strictPort", `[boolean] exit if specified port is already in use`) - .option( - "--force", - `[boolean] force the optimizer to ignore the cache and re-bundle` - ) - .action((root, options) => { - console.log(`Starting dev server at ${root || "."} with options:`, options); - }); -// Build positional completions for each command using command.args -for (const c of [cli.globalCommand, ...cli.commands]) { - // Handle options - for (const o of [...cli.globalCommand.options, ...c.options]) { - const optionKey = `${c.name} ${o.name}`; + .command("lint [...files]", "Lint project") + .action((files, options) => {}); + +const completion = await tab(cli); - if (o.rawName.includes("--logLevel ")) { - // Completion for --logLevel - flagMap.set(optionKey, async (previousArgs, toComplete) => { +for (const command of completion.commands.values()) { + if (command.name === 'lint') { + command.handler = () => { + return [ + { value: 'main.ts', description: 'Main file' }, + { value: 'index.ts', description: 'Index file' }, + ]; + }; + } + + for (const [o, config] of command.options.entries()) { + if (o === '--port') { + config.handler = () => { return [ - { action: "info", description: "Info level logging" }, - { action: "warn", description: "Warning level logging" }, - { action: "error", description: "Error level logging" }, - { action: "silent", description: "No logging" }, - ].filter((comp) => comp.action.startsWith(toComplete)); - }); + { value: '3000', description: 'Development server port' }, + { value: '8080', description: 'Alternative port' }, + ]; + }; } - - if (o.rawName.includes("--mode ")) { - // Completion for --mode - flagMap.set(optionKey, async (previousArgs, toComplete) => { + if (o === '--host') { + config.handler = () => { return [ - { action: "production", description: "Production mode" }, - { action: "development", description: "Development mode" }, - { action: "staging", description: "Staging mode" }, - ].filter((comp) => comp.action.startsWith(toComplete)); - }); + { value: 'localhost', description: 'Localhost' }, + { value: '0.0.0.0', description: 'All interfaces' }, + ]; + }; } - - if (o.rawName.includes("--port ")) { - // Completion for --port - flagMap.set(optionKey, async (previousArgs, toComplete) => { + if (o === '--config') { + config.handler = () => { return [ - { action: "3000", description: "Development server port" }, - { action: "8080", description: "Alternative port" }, - { action: "80", description: "HTTP port" }, - { action: "443", description: "HTTPS port" }, - { action: "5000", description: "Common backend port" }, - ].filter((comp) => comp.action.startsWith(toComplete)); - }); + { value: 'vite.config.ts', description: 'Vite config file' }, + { value: 'vite.config.js', description: 'Vite config file' }, + ]; + }; } - - if (o.rawName.includes("--host [host]")) { - // Completion for --host - flagMap.set(optionKey, async (previousArgs, toComplete) => { + if (o === '--mode') { + config.handler = () => { return [ - { action: "localhost", description: "Localhost" }, - { action: "0.0.0.0", description: "All interfaces" }, - { action: "127.0.0.1", description: "Loopback interface" }, - ].filter((comp) => comp.action.startsWith(toComplete)); - }); + { value: 'development', description: 'Development mode' }, + { value: 'production', description: 'Production mode' }, + ]; + }; } - - if (o.rawName.includes("--config ")) { - // Completion for --config - flagMap.set(optionKey, async (previousArgs, toComplete) => { - const configFiles = ["vite.config.ts", "vite.config.js"].filter( - (file) => file.startsWith(toComplete) - ); - return configFiles.map((file) => ({ action: file })); - }); + if (o === '--logLevel') { + config.handler = () => { + return [ + { value: 'info', description: 'Info level' }, + { value: 'warn', description: 'Warn level' }, + { value: 'error', description: 'Error level' }, + { value: 'silent', description: 'Silent level' }, + ]; + }; } - - // Add more option completions as needed - } - - // Handle positional arguments - if (c.args && c.args.length > 0) { - const positionals = c.args.map((arg) => ({ - required: arg.required, - variadic: arg.variadic, - value: arg.value, - completion: async (previousArgs, toComplete) => { - if (arg.value === "root") { - return [ - { action: "src/", description: "💣️.sh loves vite!" }, - { action: "./", description: "This one is better." }, - ]; - } - return []; - }, - })); - - positionalMap.set(c.name, positionals); } } -tab(cli); - cli.parse(); diff --git a/demo.citty.ts b/demo.citty.ts index 4ff284d..451a344 100644 --- a/demo.citty.ts +++ b/demo.citty.ts @@ -1,132 +1,126 @@ -import { defineCommand, createMain } from "citty"; -import tab from "./citty"; -import { flagMap, positionalMap } from "./shared"; - -const main = defineCommand({ - meta: { - name: "vite", - description: "Vite CLI tool", - }, - args: { - config: { type: "string", description: "Use specified config file", alias: "c" }, - base: { type: "string", description: "Public base path (default: /)" }, - logLevel: { type: "string", description: "info | warn | error | silent", alias: "l" }, - clearScreen: { type: "boolean", description: "Allow/disable clear screen when logging" }, - debug: { type: "string", description: "Show debug logs", alias: "d" }, - filter: { type: "string", description: "Filter debug logs", alias: "f" }, - mode: { type: "string", description: "Set env mode", alias: "m" }, - }, - run(ctx) { - const firstArg = ctx.args._?.[0]; - if (firstArg && devCommand?.run) { - devCommand.run({ ...ctx, args: { ...ctx.args, root: firstArg } }); - } - }, -}); - -const devCommand = defineCommand({ - meta: { - name: "dev", - description: "Start dev server", - }, - args: { - root: { type: "positional", description: "Root directory", default: "." }, - host: { type: "string", description: "Specify hostname" }, - port: { type: "string", description: "Specify port" }, - open: { type: "boolean", description: "Open browser on startup" }, - cors: { type: "boolean", description: "Enable CORS" }, - strictPort: { type: "boolean", description: "Exit if specified port is already in use" }, - force: { type: "boolean", description: "Force the optimizer to ignore the cache and re-bundle" }, - }, - run({ args }) { - const { root, port, ...options } = args; - const parsedPort = port ? parseInt(port, 10) : undefined; - - if (!root || root === ".") { - console.log("Suggested root directories:"); - console.log("- src/"); - console.log("- ./"); - } - }, -}); - -main.subCommands = { dev: devCommand }; - -for (const command of [main, ...Object.values(main.subCommands)]) { - const commandName = command.meta.name; - - for (const [argName, argConfig] of Object.entries(command.args || {})) { - const optionKey = `--${argName}`; - - if (argName === "port") { - flagMap.set(optionKey, async (_, toComplete) => { - const options = [ - { action: "3000", description: "Development server port" }, - { action: "8080", description: "Alternative port" }, - ]; - return toComplete - ? options.filter(comp => comp.action.startsWith(toComplete)) - : options; - }); - } else if (argName === "host") { - flagMap.set(optionKey, async (_, toComplete) => { - const options = [ - { action: "localhost", description: "Localhost" }, - { action: "0.0.0.0", description: "All interfaces" }, - ]; - return toComplete - ? options.filter(comp => comp.action.startsWith(toComplete)) - : options; - }); - } else if (argName === "config") { - flagMap.set(optionKey, async (_, toComplete) => { - const configFiles = ["vite.config.ts", "vite.config.js"].filter( - (file) => file.startsWith(toComplete) - ); - return configFiles.map((file) => ({ action: file })); - }); - } else if (argName === "mode") { - flagMap.set(optionKey, async (_, toComplete) => { - const options = [ - { action: "development", description: "Development mode" }, - { action: "production", description: "Production mode" }, - ]; - return toComplete - ? options.filter(comp => comp.action.startsWith(toComplete)) - : options; - }); - } else { - flagMap.set(optionKey, async (_, toComplete) => { - const flag = optionKey.startsWith("--") ? optionKey.slice(2) : optionKey; - if (!toComplete || optionKey.startsWith(toComplete)) { - return [{ action: optionKey, description: argConfig.description }]; - } - return []; - }); - } - } - - if (command.args) { - const positionals = Object.entries(command.args) - .filter(([, config]) => config.type === "positional") - .map(([argName, argConfig]) => ({ - value: argName, - required: !!argConfig.required, - completion: async (_, toComplete) => { - const options = [ - { action: "src/", description: "Source directory" }, - { action: "./", description: "Current directory" }, - ]; - return toComplete - ? options.filter(comp => comp.action.startsWith(toComplete)) - : options; - }, - })); - positionalMap.set(commandName, positionals); - } -} - -tab(main); - -const cli = createMain(main); -cli(); +import { defineCommand, createMain, CommandDef } from 'citty'; +import tab from './src/citty'; + +const main = defineCommand({ + meta: { + name: 'vite', + description: 'Vite CLI tool', + }, + args: { + config: { + type: 'string', + description: 'Use specified config file', + alias: 'c', + }, + mode: { type: 'string', description: 'Set env mode', alias: 'm' }, + logLevel: { + type: 'string', + description: 'info | warn | error | silent', + alias: 'l', + }, + }, + run(_ctx) {}, +}); + +const devCommand = defineCommand({ + meta: { + name: 'dev', + description: 'Start dev server', + }, + args: { + host: { type: 'string', description: 'Specify hostname' }, + port: { type: 'string', description: 'Specify port' }, + }, + run(ctx) { + }, +}); + +devCommand.subCommands = { + build: defineCommand({ + meta: { + name: 'build', + description: 'Build project', + }, + run({ args }) {}, + }), +}; + +const lintCommand = defineCommand({ + meta: { + name: 'lint', + description: 'Lint project', + }, + args: { + files: { type: 'positional', description: 'Files to lint' }, + }, + run(ctx) { + }, +}); + +main.subCommands = { + dev: devCommand, + lint: lintCommand, +} as Record>; + +const completion = await tab(main); + +for (const command of completion.commands.values()) { + + if (command.name === 'lint') { + command.handler = () => { + return [ + { value: 'main.ts', description: 'Main file' }, + { value: 'index.ts', description: 'Index file' }, + ]; + }; + } + + for (const [o, config] of command.options.entries()) { + if (o === '--port') { + config.handler = () => { + return [ + { value: '3000', description: 'Development server port' }, + { value: '8080', description: 'Alternative port' }, + ]; + }; + } + if (o === '--host') { + config.handler = () => { + return [ + { value: 'localhost', description: 'Localhost' }, + { value: '0.0.0.0', description: 'All interfaces' }, + ]; + }; + } + if (o === '--config') { + config.handler = () => { + return [ + { value: 'vite.config.ts', description: 'Vite config file' }, + { value: 'vite.config.js', description: 'Vite config file' }, + ]; + }; + } + if (o === '--mode') { + config.handler = () => { + return [ + { value: 'development', description: 'Development mode' }, + { value: 'production', description: 'Production mode' }, + ]; + }; + } + if (o === '--logLevel') { + config.handler = () => { + return [ + { value: 'info', description: 'Info level' }, + { value: 'warn', description: 'Warn level' }, + { value: 'error', description: 'Error level' }, + { value: 'silent', description: 'Silent level' }, + ]; + }; + } + } +} + +const cli = createMain(main); + +cli(); diff --git a/fish.ts b/fish.ts deleted file mode 100644 index 3f6a4f1..0000000 --- a/fish.ts +++ /dev/null @@ -1,2 +0,0 @@ -export function generate(name: string, exec: string) { -} diff --git a/package.json b/package.json index bb583ff..c0732e5 100644 --- a/package.json +++ b/package.json @@ -5,19 +5,28 @@ "main": "index.js", "type": "module", "scripts": { - "test": "vitest" + "test": "vitest", + "type-check": "tsc --noEmit", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@types/node": "^22.7.4", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", "cac": "^6.7.14", "citty": "^0.1.6", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "prettier": "^3.4.2", "tsx": "^4.19.1", "vitest": "^2.1.3" }, "dependencies": { "mri": "^1.2.0" - } + }, + "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a12b2b..ece142f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,625 +1,842 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - mri: - specifier: ^1.2.0 - version: 1.2.0 - -devDependencies: - '@types/node': - specifier: ^22.7.4 - version: 22.7.5 - cac: - specifier: ^6.7.14 - version: 6.7.14 - citty: - specifier: ^0.1.6 - version: 0.1.6 - tsx: - specifier: ^4.19.1 - version: 4.19.1 - vitest: - specifier: ^2.1.3 - version: 2.1.3(@types/node@22.7.5) +importers: + .: + dependencies: + mri: + specifier: ^1.2.0 + version: 1.2.0 + devDependencies: + '@types/node': + specifier: ^22.7.4 + version: 22.7.5 + '@typescript-eslint/eslint-plugin': + specifier: ^8.20.0 + version: 8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ^8.20.0 + version: 8.20.0(eslint@9.18.0)(typescript@5.7.3) + cac: + specifier: ^6.7.14 + version: 6.7.14 + citty: + specifier: ^0.1.6 + version: 0.1.6 + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.0.1(eslint@9.18.0) + eslint-plugin-prettier: + specifier: ^5.2.2 + version: 5.2.2(eslint-config-prettier@10.0.1(eslint@9.18.0))(eslint@9.18.0)(prettier@3.4.2) + prettier: + specifier: ^3.4.2 + version: 3.4.2 + tsx: + specifier: ^4.19.1 + version: 4.19.1 + vitest: + specifier: ^2.1.3 + version: 2.1.3(@types/node@22.7.5) packages: - - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.21.5': + resolution: + { + integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==, + } + engines: { node: '>=12' } cpu: [ppc64] os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/aix-ppc64@0.23.1: - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} + '@esbuild/aix-ppc64@0.23.1': + resolution: + { + integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==, + } + engines: { node: '>=18' } cpu: [ppc64] os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.21.5': + resolution: + { + integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==, + } + engines: { node: '>=12' } cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm64@0.23.1: - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} + '@esbuild/android-arm64@0.23.1': + resolution: + { + integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==, + } + engines: { node: '>=18' } cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.21.5': + resolution: + { + integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==, + } + engines: { node: '>=12' } cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.23.1: - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} + '@esbuild/android-arm@0.23.1': + resolution: + { + integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==, + } + engines: { node: '>=18' } cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.21.5': + resolution: + { + integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==, + } + engines: { node: '>=12' } cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.23.1: - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} + '@esbuild/android-x64@0.23.1': + resolution: + { + integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==, + } + engines: { node: '>=18' } cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.21.5': + resolution: + { + integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==, + } + engines: { node: '>=12' } cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.23.1: - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} + '@esbuild/darwin-arm64@0.23.1': + resolution: + { + integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==, + } + engines: { node: '>=18' } cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.21.5': + resolution: + { + integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==, + } + engines: { node: '>=12' } cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.23.1: - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} + '@esbuild/darwin-x64@0.23.1': + resolution: + { + integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==, + } + engines: { node: '>=18' } cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.21.5': + resolution: + { + integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==, + } + engines: { node: '>=12' } cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.23.1: - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} + '@esbuild/freebsd-arm64@0.23.1': + resolution: + { + integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==, + } + engines: { node: '>=18' } cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.21.5': + resolution: + { + integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==, + } + engines: { node: '>=12' } cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.23.1: - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} + '@esbuild/freebsd-x64@0.23.1': + resolution: + { + integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==, + } + engines: { node: '>=18' } cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.21.5': + resolution: + { + integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==, + } + engines: { node: '>=12' } cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.23.1: - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} + '@esbuild/linux-arm64@0.23.1': + resolution: + { + integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==, + } + engines: { node: '>=18' } cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.21.5': + resolution: + { + integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==, + } + engines: { node: '>=12' } cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.23.1: - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} + '@esbuild/linux-arm@0.23.1': + resolution: + { + integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==, + } + engines: { node: '>=18' } cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.21.5': + resolution: + { + integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==, + } + engines: { node: '>=12' } cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.23.1: - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} + '@esbuild/linux-ia32@0.23.1': + resolution: + { + integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==, + } + engines: { node: '>=18' } cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.21.5': + resolution: + { + integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==, + } + engines: { node: '>=12' } cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.23.1: - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} + '@esbuild/linux-loong64@0.23.1': + resolution: + { + integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==, + } + engines: { node: '>=18' } cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.21.5': + resolution: + { + integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==, + } + engines: { node: '>=12' } cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.23.1: - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} + '@esbuild/linux-mips64el@0.23.1': + resolution: + { + integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==, + } + engines: { node: '>=18' } cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.21.5': + resolution: + { + integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==, + } + engines: { node: '>=12' } cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.23.1: - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} + '@esbuild/linux-ppc64@0.23.1': + resolution: + { + integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==, + } + engines: { node: '>=18' } cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.21.5': + resolution: + { + integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==, + } + engines: { node: '>=12' } cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.23.1: - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} + '@esbuild/linux-riscv64@0.23.1': + resolution: + { + integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==, + } + engines: { node: '>=18' } cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.21.5': + resolution: + { + integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==, + } + engines: { node: '>=12' } cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.23.1: - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} + '@esbuild/linux-s390x@0.23.1': + resolution: + { + integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==, + } + engines: { node: '>=18' } cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.21.5': + resolution: + { + integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==, + } + engines: { node: '>=12' } cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.23.1: - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} + '@esbuild/linux-x64@0.23.1': + resolution: + { + integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==, + } + engines: { node: '>=18' } cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.21.5': + resolution: + { + integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==, + } + engines: { node: '>=12' } cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.23.1: - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} + '@esbuild/netbsd-x64@0.23.1': + resolution: + { + integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==, + } + engines: { node: '>=18' } cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-arm64@0.23.1: - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} + '@esbuild/openbsd-arm64@0.23.1': + resolution: + { + integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==, + } + engines: { node: '>=18' } cpu: [arm64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.21.5': + resolution: + { + integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==, + } + engines: { node: '>=12' } cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.23.1: - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} + '@esbuild/openbsd-x64@0.23.1': + resolution: + { + integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==, + } + engines: { node: '>=18' } cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.21.5': + resolution: + { + integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==, + } + engines: { node: '>=12' } cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.23.1: - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} + '@esbuild/sunos-x64@0.23.1': + resolution: + { + integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==, + } + engines: { node: '>=18' } cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.21.5': + resolution: + { + integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==, + } + engines: { node: '>=12' } cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.23.1: - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} + '@esbuild/win32-arm64@0.23.1': + resolution: + { + integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==, + } + engines: { node: '>=18' } cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.21.5': + resolution: + { + integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==, + } + engines: { node: '>=12' } cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.23.1: - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} + '@esbuild/win32-ia32@0.23.1': + resolution: + { + integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==, + } + engines: { node: '>=18' } cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.21.5': + resolution: + { + integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==, + } + engines: { node: '>=12' } cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.23.1: - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} + '@esbuild/win32-x64@0.23.1': + resolution: + { + integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==, + } + engines: { node: '>=18' } cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@jridgewell/sourcemap-codec@1.5.0: - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - dev: true - - /@rollup/rollup-android-arm-eabi@4.24.2: - resolution: {integrity: sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==} + '@eslint-community/eslint-utils@4.4.1': + resolution: + { + integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: + { + integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + '@eslint/config-array@0.19.1': + resolution: + { + integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/core@0.10.0': + resolution: + { + integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/eslintrc@3.2.0': + resolution: + { + integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/js@9.18.0': + resolution: + { + integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/object-schema@2.1.5': + resolution: + { + integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/plugin-kit@0.2.5': + resolution: + { + integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@humanfs/core@0.19.1': + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, + } + engines: { node: '>=18.18.0' } + + '@humanfs/node@0.16.6': + resolution: + { + integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==, + } + engines: { node: '>=18.18.0' } + + '@humanwhocodes/module-importer@1.0.1': + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: '>=12.22' } + + '@humanwhocodes/retry@0.3.1': + resolution: + { + integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==, + } + engines: { node: '>=18.18' } + + '@humanwhocodes/retry@0.4.1': + resolution: + { + integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==, + } + engines: { node: '>=18.18' } + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: + { + integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==, + } + + '@nodelib/fs.scandir@2.1.5': + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, + } + engines: { node: '>= 8' } + + '@nodelib/fs.stat@2.0.5': + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, + } + engines: { node: '>= 8' } + + '@nodelib/fs.walk@1.2.8': + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, + } + engines: { node: '>= 8' } + + '@pkgr/core@0.1.1': + resolution: + { + integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==, + } + engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + + '@rollup/rollup-android-arm-eabi@4.24.2': + resolution: + { + integrity: sha512-ufoveNTKDg9t/b7nqI3lwbCG/9IJMhADBNjjz/Jn6LxIZxD7T5L8l2uO/wD99945F1Oo8FvgbbZJRguyk/BdzA==, + } cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-android-arm64@4.24.2: - resolution: {integrity: sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==} + '@rollup/rollup-android-arm64@4.24.2': + resolution: + { + integrity: sha512-iZoYCiJz3Uek4NI0J06/ZxUgwAfNzqltK0MptPDO4OR0a88R4h0DSELMsflS6ibMCJ4PnLvq8f7O1d7WexUvIA==, + } cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-arm64@4.24.2: - resolution: {integrity: sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==} + '@rollup/rollup-darwin-arm64@4.24.2': + resolution: + { + integrity: sha512-/UhrIxobHYCBfhi5paTkUDQ0w+jckjRZDZ1kcBL132WeHZQ6+S5v9jQPVGLVrLbNUebdIRpIt00lQ+4Z7ys4Rg==, + } cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-darwin-x64@4.24.2: - resolution: {integrity: sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==} + '@rollup/rollup-darwin-x64@4.24.2': + resolution: + { + integrity: sha512-1F/jrfhxJtWILusgx63WeTvGTwE4vmsT9+e/z7cZLKU8sBMddwqw3UV5ERfOV+H1FuRK3YREZ46J4Gy0aP3qDA==, + } cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-freebsd-arm64@4.24.2: - resolution: {integrity: sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==} + '@rollup/rollup-freebsd-arm64@4.24.2': + resolution: + { + integrity: sha512-1YWOpFcGuC6iGAS4EI+o3BV2/6S0H+m9kFOIlyFtp4xIX5rjSnL3AwbTBxROX0c8yWtiWM7ZI6mEPTI7VkSpZw==, + } cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-freebsd-x64@4.24.2: - resolution: {integrity: sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==} + '@rollup/rollup-freebsd-x64@4.24.2': + resolution: + { + integrity: sha512-3qAqTewYrCdnOD9Gl9yvPoAoFAVmPJsBvleabvx4bnu1Kt6DrB2OALeRVag7BdWGWLhP1yooeMLEi6r2nYSOjg==, + } cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.24.2: - resolution: {integrity: sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==} + '@rollup/rollup-linux-arm-gnueabihf@4.24.2': + resolution: + { + integrity: sha512-ArdGtPHjLqWkqQuoVQ6a5UC5ebdX8INPuJuJNWRe0RGa/YNhVvxeWmCTFQ7LdmNCSUzVZzxAvUznKaYx645Rig==, + } cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm-musleabihf@4.24.2: - resolution: {integrity: sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==} + '@rollup/rollup-linux-arm-musleabihf@4.24.2': + resolution: + { + integrity: sha512-B6UHHeNnnih8xH6wRKB0mOcJGvjZTww1FV59HqJoTJ5da9LCG6R4SEBt6uPqzlawv1LoEXSS0d4fBlHNWl6iYw==, + } cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-gnu@4.24.2: - resolution: {integrity: sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==} + '@rollup/rollup-linux-arm64-gnu@4.24.2': + resolution: + { + integrity: sha512-kr3gqzczJjSAncwOS6i7fpb4dlqcvLidqrX5hpGBIM1wtt0QEVtf4wFaAwVv8QygFU8iWUMYEoJZWuWxyua4GQ==, + } cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-arm64-musl@4.24.2: - resolution: {integrity: sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==} + '@rollup/rollup-linux-arm64-musl@4.24.2': + resolution: + { + integrity: sha512-TDdHLKCWgPuq9vQcmyLrhg/bgbOvIQ8rtWQK7MRxJ9nvaxKx38NvY7/Lo6cYuEnNHqf6rMqnivOIPIQt6H2AoA==, + } cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.24.2: - resolution: {integrity: sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.24.2': + resolution: + { + integrity: sha512-xv9vS648T3X4AxFFZGWeB5Dou8ilsv4VVqJ0+loOIgDO20zIhYfDLkk5xoQiej2RiSQkld9ijF/fhLeonrz2mw==, + } cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.24.2: - resolution: {integrity: sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==} + '@rollup/rollup-linux-riscv64-gnu@4.24.2': + resolution: + { + integrity: sha512-tbtXwnofRoTt223WUZYiUnbxhGAOVul/3StZ947U4A5NNjnQJV5irKMm76G0LGItWs6y+SCjUn/Q0WaMLkEskg==, + } cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-s390x-gnu@4.24.2: - resolution: {integrity: sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==} + '@rollup/rollup-linux-s390x-gnu@4.24.2': + resolution: + { + integrity: sha512-gc97UebApwdsSNT3q79glOSPdfwgwj5ELuiyuiMY3pEWMxeVqLGKfpDFoum4ujivzxn6veUPzkGuSYoh5deQ2Q==, + } cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-gnu@4.24.2: - resolution: {integrity: sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==} + '@rollup/rollup-linux-x64-gnu@4.24.2': + resolution: + { + integrity: sha512-jOG/0nXb3z+EM6SioY8RofqqmZ+9NKYvJ6QQaa9Mvd3RQxlH68/jcB/lpyVt4lCiqr04IyaC34NzhUqcXbB5FQ==, + } cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-linux-x64-musl@4.24.2: - resolution: {integrity: sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==} + '@rollup/rollup-linux-x64-musl@4.24.2': + resolution: + { + integrity: sha512-XAo7cJec80NWx9LlZFEJQxqKOMz/lX3geWs2iNT5CHIERLFfd90f3RYLLjiCBm1IMaQ4VOX/lTC9lWfzzQm14Q==, + } cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-arm64-msvc@4.24.2: - resolution: {integrity: sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==} + '@rollup/rollup-win32-arm64-msvc@4.24.2': + resolution: + { + integrity: sha512-A+JAs4+EhsTjnPQvo9XY/DC0ztaws3vfqzrMNMKlwQXuniBKOIIvAAI8M0fBYiTCxQnElYu7mLk7JrhlQ+HeOw==, + } cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-ia32-msvc@4.24.2: - resolution: {integrity: sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==} + '@rollup/rollup-win32-ia32-msvc@4.24.2': + resolution: + { + integrity: sha512-ZhcrakbqA1SCiJRMKSU64AZcYzlZ/9M5LaYil9QWxx9vLnkQ9Vnkve17Qn4SjlipqIIBFKjBES6Zxhnvh0EAEw==, + } cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@rollup/rollup-win32-x64-msvc@4.24.2: - resolution: {integrity: sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==} + '@rollup/rollup-win32-x64-msvc@4.24.2': + resolution: + { + integrity: sha512-2mLH46K1u3r6uwc95hU+OR9q/ggYMpnS7pSp83Ece1HUQgF9Nh/QwTK5rcgbFnV9j+08yBrU5sA/P0RK2MSBNA==, + } cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - - /@types/estree@1.0.6: - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - dev: true - - /@types/node@22.7.5: - resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} - dependencies: - undici-types: 6.19.8 - dev: true - - /@vitest/expect@2.1.3: - resolution: {integrity: sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==} - dependencies: - '@vitest/spy': 2.1.3 - '@vitest/utils': 2.1.3 - chai: 5.1.2 - tinyrainbow: 1.2.0 - dev: true - /@vitest/mocker@2.1.3(@vitest/spy@2.1.3)(vite@5.4.10): - resolution: {integrity: sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==} + '@types/estree@1.0.6': + resolution: + { + integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==, + } + + '@types/json-schema@7.0.15': + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + '@types/node@22.7.5': + resolution: + { + integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==, + } + + '@typescript-eslint/eslint-plugin@8.20.0': + resolution: + { + integrity: sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/parser@8.20.0': + resolution: + { + integrity: sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/scope-manager@8.20.0': + resolution: + { + integrity: sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/type-utils@8.20.0': + resolution: + { + integrity: sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/types@8.20.0': + resolution: + { + integrity: sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/typescript-estree@8.20.0': + resolution: + { + integrity: sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/utils@8.20.0': + resolution: + { + integrity: sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/visitor-keys@8.20.0': + resolution: + { + integrity: sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@vitest/expect@2.1.3': + resolution: + { + integrity: sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==, + } + + '@vitest/mocker@2.1.3': + resolution: + { + integrity: sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==, + } peerDependencies: '@vitest/spy': 2.1.3 msw: ^2.3.5 @@ -629,343 +846,927 @@ packages: optional: true vite: optional: true - dependencies: - '@vitest/spy': 2.1.3 - estree-walker: 3.0.3 - magic-string: 0.30.12 - vite: 5.4.10(@types/node@22.7.5) - dev: true - - /@vitest/pretty-format@2.1.3: - resolution: {integrity: sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==} - dependencies: - tinyrainbow: 1.2.0 - dev: true - - /@vitest/runner@2.1.3: - resolution: {integrity: sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==} - dependencies: - '@vitest/utils': 2.1.3 - pathe: 1.1.2 - dev: true - - /@vitest/snapshot@2.1.3: - resolution: {integrity: sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==} - dependencies: - '@vitest/pretty-format': 2.1.3 - magic-string: 0.30.12 - pathe: 1.1.2 - dev: true - - /@vitest/spy@2.1.3: - resolution: {integrity: sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==} - dependencies: - tinyspy: 3.0.2 - dev: true - - /@vitest/utils@2.1.3: - resolution: {integrity: sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==} - dependencies: - '@vitest/pretty-format': 2.1.3 - loupe: 3.1.2 - tinyrainbow: 1.2.0 - dev: true - - /assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - dev: true - - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true - /chai@5.1.2: - resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} - engines: {node: '>=12'} - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.2 - pathval: 2.0.0 - dev: true - - /check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} - dev: true - - /citty@0.1.6: - resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - dependencies: - consola: 3.2.3 - dev: true - - /consola@3.2.3: - resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} - engines: {node: ^14.18.0 || >=16.10.0} - dev: true + '@vitest/pretty-format@2.1.3': + resolution: + { + integrity: sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==, + } + + '@vitest/runner@2.1.3': + resolution: + { + integrity: sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==, + } + + '@vitest/snapshot@2.1.3': + resolution: + { + integrity: sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==, + } + + '@vitest/spy@2.1.3': + resolution: + { + integrity: sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==, + } + + '@vitest/utils@2.1.3': + resolution: + { + integrity: sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==, + } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.0: + resolution: + { + integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==, + } + engines: { node: '>=0.4.0' } + hasBin: true - /debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} + ajv@6.12.6: + resolution: + { + integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, + } + + ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: '>=8' } + + argparse@2.0.1: + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } + + assertion-error@2.0.1: + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, + } + engines: { node: '>=12' } + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + brace-expansion@1.1.11: + resolution: + { + integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==, + } + + brace-expansion@2.0.1: + resolution: + { + integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==, + } + + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: '>=8' } + + cac@6.7.14: + resolution: + { + integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, + } + engines: { node: '>=8' } + + callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: '>=6' } + + chai@5.1.2: + resolution: + { + integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==, + } + engines: { node: '>=12' } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: '>=10' } + + check-error@2.1.1: + resolution: + { + integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==, + } + engines: { node: '>= 16' } + + citty@0.1.6: + resolution: + { + integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==, + } + + color-convert@2.0.1: + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: '>=7.0.0' } + + color-name@1.1.4: + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } + + concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } + + consola@3.2.3: + resolution: + { + integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==, + } + engines: { node: ^14.18.0 || >=16.10.0 } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: '>= 8' } + + debug@4.3.7: + resolution: + { + integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, + } + engines: { node: '>=6.0' } peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - dependencies: - ms: 2.1.3 - dev: true - /deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - dev: true + deep-eql@5.0.2: + resolution: + { + integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==, + } + engines: { node: '>=6' } + + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } + + esbuild@0.21.5: + resolution: + { + integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==, + } + engines: { node: '>=12' } + hasBin: true - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.23.1: + resolution: + { + integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==, + } + engines: { node: '>=18' } hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - dev: true - /esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: '>=10' } + + eslint-config-prettier@10.0.1: + resolution: + { + integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==, + } hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - dev: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.2.2: + resolution: + { + integrity: sha512-1yI3/hf35wmlq66C8yOyrujQnel+v5l1Vop5Cl2I6ylyNTT1JbuUUnV3/41PzwTzcyDp/oF0jWE3HXvcH5AQOQ==, + } + engines: { node: ^14.18.0 || >=16.0.0 } + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - dependencies: - '@types/estree': 1.0.6 - dev: true + eslint-scope@8.2.0: + resolution: + { + integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.0: + resolution: + { + integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint@9.18.0: + resolution: + { + integrity: sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + espree@10.3.0: + resolution: + { + integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esquery@1.6.0: + resolution: + { + integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, + } + engines: { node: '>=0.10' } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: '>=4.0' } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: '>=4.0' } + + estree-walker@3.0.3: + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, + } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: '>=0.10.0' } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-diff@1.3.0: + resolution: + { + integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==, + } + + fast-glob@3.3.3: + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, + } + engines: { node: '>=8.6.0' } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } + + fastq@1.18.0: + resolution: + { + integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==, + } + + file-entry-cache@8.0.0: + resolution: + { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, + } + engines: { node: '>=16.0.0' } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: '>=8' } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: '>=10' } + + flat-cache@4.0.1: + resolution: + { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, + } + engines: { node: '>=16' } + + flatted@3.3.2: + resolution: + { + integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==, + } + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] - requiresBuild: true - dev: true - optional: true - - /get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: true - /loupe@3.1.2: - resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} - dev: true - - /magic-string@0.30.12: - resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: false - - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true - - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + get-tsconfig@4.8.1: + resolution: + { + integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==, + } + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: '>= 6' } + + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: '>=10.13.0' } + + globals@14.0.0: + resolution: + { + integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, + } + engines: { node: '>=18' } + + graphemer@1.4.0: + resolution: + { + integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, + } + + has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: '>=8' } + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: '>= 4' } + + import-fresh@3.3.0: + resolution: + { + integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==, + } + engines: { node: '>=6' } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: '>=0.8.19' } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: '>=0.10.0' } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: '>=0.10.0' } + + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: '>=0.12.0' } + + isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + + js-yaml@4.1.0: + resolution: + { + integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, + } hasBin: true - dev: true - - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true - - /pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} - engines: {node: '>= 14.16'} - dev: true - /picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - dev: true + json-buffer@3.0.1: + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } + + keyv@4.5.4: + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, + } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: '>= 0.8.0' } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: '>=10' } + + lodash.merge@4.6.2: + resolution: + { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + } + + loupe@3.1.2: + resolution: + { + integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==, + } + + magic-string@0.30.12: + resolution: + { + integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==, + } + + merge2@1.4.1: + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, + } + engines: { node: '>= 8' } + + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: '>=8.6' } + + minimatch@3.1.2: + resolution: + { + integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, + } + + minimatch@9.0.5: + resolution: + { + integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, + } + engines: { node: '>=16 || 14 >=14.17' } + + mri@1.2.0: + resolution: + { + integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==, + } + engines: { node: '>=4' } + + ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + nanoid@3.3.7: + resolution: + { + integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true - /postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.7 - picocolors: 1.1.1 - source-map-js: 1.2.1 - dev: true + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: '>= 0.8.0' } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: '>=10' } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: '>=10' } + + parent-module@1.0.1: + resolution: + { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, + } + engines: { node: '>=6' } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: '>=8' } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: '>=8' } + + pathe@1.1.2: + resolution: + { + integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==, + } + + pathval@2.0.0: + resolution: + { + integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==, + } + engines: { node: '>= 14.16' } + + picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } + + picomatch@2.3.1: + resolution: + { + integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, + } + engines: { node: '>=8.6' } + + postcss@8.4.47: + resolution: + { + integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==, + } + engines: { node: ^10 || ^12 || >=14 } + + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: '>= 0.8.0' } + + prettier-linter-helpers@1.0.0: + resolution: + { + integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==, + } + engines: { node: '>=6.0.0' } + + prettier@3.4.2: + resolution: + { + integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==, + } + engines: { node: '>=14' } + hasBin: true - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: '>=6' } + + queue-microtask@1.2.3: + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, + } + + resolve-from@4.0.0: + resolution: + { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, + } + engines: { node: '>=4' } + + resolve-pkg-maps@1.0.0: + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, + } + + reusify@1.0.4: + resolution: + { + integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==, + } + engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + + rollup@4.24.2: + resolution: + { + integrity: sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==, + } + engines: { node: '>=18.0.0', npm: '>=8.0.0' } + hasBin: true - /rollup@4.24.2: - resolution: {integrity: sha512-do/DFGq5g6rdDhdpPq5qb2ecoczeK6y+2UAjdJ5trjQJj5f1AiVdLRWRc9A9/fFukfvJRgM0UXzxBIYMovm5ww==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + run-parallel@1.2.0: + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, + } + + semver@7.6.3: + resolution: + { + integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==, + } + engines: { node: '>=10' } hasBin: true - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.2 - '@rollup/rollup-android-arm64': 4.24.2 - '@rollup/rollup-darwin-arm64': 4.24.2 - '@rollup/rollup-darwin-x64': 4.24.2 - '@rollup/rollup-freebsd-arm64': 4.24.2 - '@rollup/rollup-freebsd-x64': 4.24.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.2 - '@rollup/rollup-linux-arm-musleabihf': 4.24.2 - '@rollup/rollup-linux-arm64-gnu': 4.24.2 - '@rollup/rollup-linux-arm64-musl': 4.24.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.2 - '@rollup/rollup-linux-riscv64-gnu': 4.24.2 - '@rollup/rollup-linux-s390x-gnu': 4.24.2 - '@rollup/rollup-linux-x64-gnu': 4.24.2 - '@rollup/rollup-linux-x64-musl': 4.24.2 - '@rollup/rollup-win32-arm64-msvc': 4.24.2 - '@rollup/rollup-win32-ia32-msvc': 4.24.2 - '@rollup/rollup-win32-x64-msvc': 4.24.2 - fsevents: 2.3.3 - dev: true - - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true - - /source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - dev: true - - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true - - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true - - /tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - dev: true - - /tinyexec@0.3.1: - resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} - dev: true - - /tinypool@1.0.1: - resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} - engines: {node: ^18.0.0 || >=20.0.0} - dev: true - - /tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - dev: true - - /tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} - engines: {node: '>=14.0.0'} - dev: true - - /tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} - engines: {node: '>=18.0.0'} + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: '>=8' } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: '>=8' } + + siginfo@2.0.0: + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, + } + + source-map-js@1.2.1: + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: '>=0.10.0' } + + stackback@0.0.2: + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, + } + + std-env@3.7.0: + resolution: + { + integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==, + } + + strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, + } + engines: { node: '>=8' } + + supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: '>=8' } + + synckit@0.9.2: + resolution: + { + integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==, + } + engines: { node: ^14.18.0 || >=16.0.0 } + + tinybench@2.9.0: + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, + } + + tinyexec@0.3.1: + resolution: + { + integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==, + } + + tinypool@1.0.1: + resolution: + { + integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==, + } + engines: { node: ^18.0.0 || >=20.0.0 } + + tinyrainbow@1.2.0: + resolution: + { + integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==, + } + engines: { node: '>=14.0.0' } + + tinyspy@3.0.2: + resolution: + { + integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==, + } + engines: { node: '>=14.0.0' } + + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: '>=8.0' } + + ts-api-utils@2.0.0: + resolution: + { + integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==, + } + engines: { node: '>=18.12' } + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } + + tsx@4.19.1: + resolution: + { + integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==, + } + engines: { node: '>=18.0.0' } hasBin: true - dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - dev: true + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: '>= 0.8.0' } + + typescript@5.7.3: + resolution: + { + integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==, + } + engines: { node: '>=14.17' } + hasBin: true - /vite-node@2.1.3(@types/node@22.7.5): - resolution: {integrity: sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==} - engines: {node: ^18.0.0 || >=20.0.0} + undici-types@6.19.8: + resolution: + { + integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==, + } + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + vite-node@2.1.3: + resolution: + { + integrity: sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==, + } + engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.7 - pathe: 1.1.2 - vite: 5.4.10(@types/node@22.7.5) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - dev: true - /vite@5.4.10(@types/node@22.7.5): - resolution: {integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@5.4.10: + resolution: + { + integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==, + } + engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true peerDependencies: '@types/node': ^18.0.0 || >=20.0.0 @@ -993,43 +1794,993 @@ packages: optional: true terser: optional: true - dependencies: - '@types/node': 22.7.5 - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.24.2 + + vitest@2.1.3: + resolution: + { + integrity: sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==, + } + engines: { node: ^18.0.0 || >=20.0.0 } + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.3 + '@vitest/ui': 2.1.3 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: '>= 8' } + hasBin: true + + why-is-node-running@2.3.0: + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, + } + engines: { node: '>=8' } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: '>=0.10.0' } + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: '>=10' } + +snapshots: + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@9.18.0)': + dependencies: + eslint: 9.18.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.19.1': + dependencies: + '@eslint/object-schema': 2.1.5 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.10.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.2.0': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.18.0': {} + + '@eslint/object-schema@2.1.5': {} + + '@eslint/plugin-kit@0.2.5': + dependencies: + '@eslint/core': 0.10.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.18.0 + + '@pkgr/core@0.1.1': {} + + '@rollup/rollup-android-arm-eabi@4.24.2': + optional: true + + '@rollup/rollup-android-arm64@4.24.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.24.2': + optional: true + + '@rollup/rollup-darwin-x64@4.24.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.24.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.24.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.24.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.24.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.24.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.24.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.24.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.24.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.24.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.24.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.24.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.24.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.24.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.24.2': + optional: true + + '@types/estree@1.0.6': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + + '@typescript-eslint/eslint-plugin@8.20.0(@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.20.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.20.0 + '@typescript-eslint/type-utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.20.0 + eslint: 9.18.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.20.0(eslint@9.18.0)(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.20.0 + '@typescript-eslint/types': 8.20.0 + '@typescript-eslint/typescript-estree': 8.20.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.20.0 + debug: 4.3.7 + eslint: 9.18.0 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.20.0': + dependencies: + '@typescript-eslint/types': 8.20.0 + '@typescript-eslint/visitor-keys': 8.20.0 + + '@typescript-eslint/type-utils@8.20.0(eslint@9.18.0)(typescript@5.7.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.20.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.20.0(eslint@9.18.0)(typescript@5.7.3) + debug: 4.3.7 + eslint: 9.18.0 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.20.0': {} + + '@typescript-eslint/typescript-estree@8.20.0(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.20.0 + '@typescript-eslint/visitor-keys': 8.20.0 + debug: 4.3.7 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.20.0(eslint@9.18.0)(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0) + '@typescript-eslint/scope-manager': 8.20.0 + '@typescript-eslint/types': 8.20.0 + '@typescript-eslint/typescript-estree': 8.20.0(typescript@5.7.3) + eslint: 9.18.0 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.20.0': + dependencies: + '@typescript-eslint/types': 8.20.0 + eslint-visitor-keys: 4.2.0 + + '@vitest/expect@2.1.3': + dependencies: + '@vitest/spy': 2.1.3 + '@vitest/utils': 2.1.3 + chai: 5.1.2 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.3(@vitest/spy@2.1.3)(vite@5.4.10(@types/node@22.7.5))': + dependencies: + '@vitest/spy': 2.1.3 + estree-walker: 3.0.3 + magic-string: 0.30.12 + optionalDependencies: + vite: 5.4.10(@types/node@22.7.5) + + '@vitest/pretty-format@2.1.3': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.3': + dependencies: + '@vitest/utils': 2.1.3 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.3': + dependencies: + '@vitest/pretty-format': 2.1.3 + magic-string: 0.30.12 + pathe: 1.1.2 + + '@vitest/spy@2.1.3': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.3': + dependencies: + '@vitest/pretty-format': 2.1.3 + loupe: 3.1.2 + tinyrainbow: 1.2.0 + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + assertion-error@2.0.1: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + cac@6.7.14: {} + + callsites@3.1.0: {} + + chai@5.1.2: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.2 + pathval: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@2.1.1: {} + + citty@0.1.6: + dependencies: + consola: 3.2.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + consola@3.2.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.0.1(eslint@9.18.0): + dependencies: + eslint: 9.18.0 + + eslint-plugin-prettier@5.2.2(eslint-config-prettier@10.0.1(eslint@9.18.0))(eslint@9.18.0)(prettier@3.4.2): + dependencies: + eslint: 9.18.0 + prettier: 3.4.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.2 + optionalDependencies: + eslint-config-prettier: 10.0.1(eslint@9.18.0) + + eslint-scope@8.2.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.18.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.1 + '@eslint/core': 0.10.0 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.18.0 + '@eslint/plugin-kit': 0.2.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.3.7 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + 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.4 + transitivePeerDependencies: + - supports-color + + espree@10.3.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.18.0: + dependencies: + reusify: 1.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + + flatted@3.3.2: {} + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.8.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loupe@3.1.2: {} + + magic-string@0.30.12: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + mri@1.2.0: {} + + ms@2.1.3: {} + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + pathe@1.1.2: {} + + pathval@2.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.4.2: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + reusify@1.0.4: {} + + rollup@4.24.2: + dependencies: + '@types/estree': 1.0.6 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.24.2 + '@rollup/rollup-android-arm64': 4.24.2 + '@rollup/rollup-darwin-arm64': 4.24.2 + '@rollup/rollup-darwin-x64': 4.24.2 + '@rollup/rollup-freebsd-arm64': 4.24.2 + '@rollup/rollup-freebsd-x64': 4.24.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.2 + '@rollup/rollup-linux-arm-musleabihf': 4.24.2 + '@rollup/rollup-linux-arm64-gnu': 4.24.2 + '@rollup/rollup-linux-arm64-musl': 4.24.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.2 + '@rollup/rollup-linux-riscv64-gnu': 4.24.2 + '@rollup/rollup-linux-s390x-gnu': 4.24.2 + '@rollup/rollup-linux-x64-gnu': 4.24.2 + '@rollup/rollup-linux-x64-musl': 4.24.2 + '@rollup/rollup-win32-arm64-msvc': 4.24.2 + '@rollup/rollup-win32-ia32-msvc': 4.24.2 + '@rollup/rollup-win32-x64-msvc': 4.24.2 fsevents: 2.3.3 - dev: true - /vitest@2.1.3(@types/node@22.7.5): - resolution: {integrity: sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.3 - '@vitest/ui': 2.1.3 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + semver@7.6.3: {} + + shebang-command@2.0.0: dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.7.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + synckit@0.9.2: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.8.1 + + tinybench@2.9.0: {} + + tinyexec@0.3.1: {} + + tinypool@1.0.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.0.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + tslib@2.8.1: {} + + tsx@4.19.1: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@5.7.3: {} + + undici-types@6.19.8: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite-node@2.1.3(@types/node@22.7.5): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + pathe: 1.1.2 + vite: 5.4.10(@types/node@22.7.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.10(@types/node@22.7.5): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.24.2 + optionalDependencies: '@types/node': 22.7.5 + fsevents: 2.3.3 + + vitest@2.1.3(@types/node@22.7.5): + dependencies: '@vitest/expect': 2.1.3 - '@vitest/mocker': 2.1.3(@vitest/spy@2.1.3)(vite@5.4.10) + '@vitest/mocker': 2.1.3(@vitest/spy@2.1.3)(vite@5.4.10(@types/node@22.7.5)) '@vitest/pretty-format': 2.1.3 '@vitest/runner': 2.1.3 '@vitest/snapshot': 2.1.3 @@ -1047,6 +2798,8 @@ packages: vite: 5.4.10(@types/node@22.7.5) vite-node: 2.1.3(@types/node@22.7.5) why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.7.5 transitivePeerDependencies: - less - lightningcss @@ -1057,13 +2810,16 @@ packages: - sugarss - supports-color - terser - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} diff --git a/shadcn.cac.ts b/shadcn.cac.ts index d9093ed..9074d70 100644 --- a/shadcn.cac.ts +++ b/shadcn.cac.ts @@ -1,104 +1,103 @@ -import fs from "fs/promises"; -import cac from "cac"; -import { - Callback, - Completion, - flagMap, - Positional, - positionalMap, -} from "./shared"; -import path from "path"; -import tab from "./cac"; +// import fs from "fs/promises"; +// import cac from "cac"; +// import { +// Callback, +// Completion, +// flagMap, +// Positional, +// positionalMap, +// } from "./shared"; +// import path from "path"; +// import tab from "./cac"; -const cli = cac("shadcn"); // Using 'shadcn' as the CLI tool name +// const cli = cac("shadcn"); // Using 'shadcn' as the CLI tool name -// Global options -cli - .option("-c, --cwd [cwd]", `[string] the working directory. defaults to the current directory.`) - .option("-h, --help", `display help for command`); +// // Global options +// cli +// .option("-c, --cwd [cwd]", `[string] the working directory. defaults to the current directory.`) +// .option("-h, --help", `display help for command`); -// Init command -cli - .command("init", "initialize your project and install dependencies") - .option("-d, --defaults", `[boolean] use default values i.e new-york, zinc, and css variables`, { default: false }) - .option("-f, --force", `[boolean] force overwrite of existing components.json`, { default: false }) - .option("-y, --yes", `[boolean] skip confirmation prompt`, { default: false }) - .action((options) => { - console.log(`Initializing project with options:`, options); - }); +// // Init command +// cli +// .command("init", "initialize your project and install dependencies") +// .option("-d, --defaults", `[boolean] use default values i.e new-york, zinc, and css variables`, { default: false }) +// .option("-f, --force", `[boolean] force overwrite of existing components.json`, { default: false }) +// .option("-y, --yes", `[boolean] skip confirmation prompt`, { default: false }) +// .action((options) => { +// console.log(`Initializing project with options:`, options); +// }); -// Add command -cli - .command("add [...components]", "add a component to your project") - .option("-y, --yes", `[boolean] skip confirmation prompt`, { default: false }) - .option("-o, --overwrite", `[boolean] overwrite existing files`, { default: false }) - .option("-a, --all", `[boolean] add all available components`, { default: false }) - .option("-p, --path [path]", `[string] the path to add the component to`) - .action((components, options) => { - console.log(`Adding components:`, components, `with options:`, options); - }); +// // Add command +// cli +// .command("add [...components]", "add a component to your project") +// .option("-y, --yes", `[boolean] skip confirmation prompt`, { default: false }) +// .option("-o, --overwrite", `[boolean] overwrite existing files`, { default: false }) +// .option("-a, --all", `[boolean] add all available components`, { default: false }) +// .option("-p, --path [path]", `[string] the path to add the component to`) +// .action((components, options) => { +// console.log(`Adding components:`, components, `with options:`, options); +// }); -// Build positional completions for each command using command.args -for (const c of [cli.globalCommand, ...cli.commands]) { - // Handle options - for (const o of [...cli.globalCommand.options, ...c.options]) { - const optionKey = `${c.name} ${o.name}`; +// // Build positional completions for each command using command.args +// for (const c of [cli.globalCommand, ...cli.commands]) { +// // Handle options +// for (const o of [...cli.globalCommand.options, ...c.options]) { +// const optionKey = `${c.name} ${o.name}`; - if (o.rawName.includes("--cwd ")) { - // Completion for --cwd (common working directories) - flagMap.set(optionKey, async (previousArgs, toComplete) => { - return [ - { action: "./apps/www", description: "Default app directory" }, - { action: "./apps/admin", description: "Admin app directory" }, - ].filter((comp) => comp.action.startsWith(toComplete)); - }); - } +// if (o.rawName.includes("--cwd ")) { +// // Completion for --cwd (common working directories) +// flagMap.set(optionKey, async (previousArgs, toComplete) => { +// return [ +// { action: "./apps/www", description: "Default app directory" }, +// { action: "./apps/admin", description: "Admin app directory" }, +// ].filter((comp) => comp.action.startsWith(toComplete)); +// }); +// } - if (o.rawName.includes("--defaults")) { - // Completion for --defaults (show info for default setup) - flagMap.set(optionKey, async (previousArgs, toComplete) => { - return [{ action: "true", description: "Use default values for setup" }]; - }); - } +// if (o.rawName.includes("--defaults")) { +// // Completion for --defaults (show info for default setup) +// flagMap.set(optionKey, async (previousArgs, toComplete) => { +// return [{ action: "true", description: "Use default values for setup" }]; +// }); +// } - if (o.rawName.includes("--path ")) { - // Completion for --path (common component paths) - flagMap.set(optionKey, async (previousArgs, toComplete) => { - return [ - { action: "src/components", description: "Main components directory" }, - { action: "src/ui", description: "UI components directory" }, - ].filter((comp) => comp.action.startsWith(toComplete)); - }); - } - } +// if (o.rawName.includes("--path ")) { +// // Completion for --path (common component paths) +// flagMap.set(optionKey, async (previousArgs, toComplete) => { +// return [ +// { action: "src/components", description: "Main components directory" }, +// { action: "src/ui", description: "UI components directory" }, +// ].filter((comp) => comp.action.startsWith(toComplete)); +// }); +// } +// } - // Handle positional arguments - if (c.name === "add" && c.args && c.args.length > 0) { - const componentChoices = [ - "accordion", "alert", "alert-dialog", "aspect-ratio", "avatar", - "badge", "button", "calendar", "card", "checkbox" - ]; - const positionals = c.args.map((arg) => ({ - required: arg.required, - variadic: arg.variadic, - value: arg.value, - completion: async (previousArgs, toComplete) => { - // if (arg.value === "root") { - return componentChoices - // TODO: a bug here that toComplete is equal to "add" which then makes filter not work, we should omit toComplete and add it to previous args if the endsWithSpace is true - // .filter((comp) => comp.startsWith(toComplete)) - .map((comp) => ({ action: comp, description: `Add ${comp} component` })); - // } - // return []; - }, - })); +// // Handle positional arguments +// if (c.name === "add" && c.args && c.args.length > 0) { +// const componentChoices = [ +// "accordion", "alert", "alert-dialog", "aspect-ratio", "avatar", +// "badge", "button", "calendar", "card", "checkbox" +// ]; +// const positionals = c.args.map((arg) => ({ +// required: arg.required, +// variadic: arg.variadic, +// value: arg.value, +// completion: async (previousArgs, toComplete) => { +// // if (arg.value === "root") { +// return componentChoices +// // TODO: a bug here that toComplete is equal to "add" which then makes filter not work, we should omit toComplete and add it to previous args if the endsWithSpace is true +// // .filter((comp) => comp.startsWith(toComplete)) +// .map((comp) => ({ action: comp, description: `Add ${comp} component` })); +// // } +// // return []; +// }, +// })); - positionalMap.set(c.name, positionals); - } -} +// positionalMap.set(c.name, positionals); +// } +// } -// Initialize tab completion -tab(cli); - -cli.parse(); +// // Initialize tab completion +// tab(cli); +// cli.parse(); diff --git a/shared.ts b/shared.ts deleted file mode 100644 index 5925d23..0000000 --- a/shared.ts +++ /dev/null @@ -1,75 +0,0 @@ - -// ShellCompRequestCmd is the name of the hidden command that is used to request -// completion results from the program. It is used by the shell completion scripts. -export const ShellCompRequestCmd: string = "__complete"; - -// ShellCompNoDescRequestCmd is the name of the hidden command that is used to request -// completion results without their description. It is used by the shell completion scripts. -export const ShellCompNoDescRequestCmd: string = "__completeNoDesc"; - -// ShellCompDirective is a bit map representing the different behaviors the shell -// can be instructed to have once completions have been provided. -export const ShellCompDirective = { - // ShellCompDirectiveError indicates an error occurred and completions should be ignored. - ShellCompDirectiveError: 1 << 0, - - // ShellCompDirectiveNoSpace indicates that the shell should not add a space - // after the completion even if there is a single completion provided. - ShellCompDirectiveNoSpace: 1 << 1, - - // ShellCompDirectiveNoFileComp indicates that the shell should not provide - // file completion even when no completion is provided. - ShellCompDirectiveNoFileComp: 1 << 2, - - // ShellCompDirectiveFilterFileExt indicates that the provided completions - // should be used as file extension filters. - // For flags, using Command.MarkFlagFilename() and Command.MarkPersistentFlagFilename() - // is a shortcut to using this directive explicitly. The BashCompFilenameExt - // annotation can also be used to obtain the same behavior for flags. - ShellCompDirectiveFilterFileExt: 1 << 3, - - // ShellCompDirectiveFilterDirs indicates that only directory names should - // be provided in file completion. To request directory names within another - // directory, the returned completions should specify the directory within - // which to search. The BashCompSubdirsInDir annotation can be used to - // obtain the same behavior but only for flags. - ShellCompDirectiveFilterDirs: 1 << 4, - - // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order - // in which the completions are provided. - ShellCompDirectiveKeepOrder: 1 << 5, - - // =========================================================================== - - // All directives using iota (or equivalent in Go) should be above this one. - // For internal use. - shellCompDirectiveMaxValue: 1 << 6, - - // ShellCompDirectiveDefault indicates to let the shell perform its default - // behavior after completions have been provided. - // This one must be last to avoid messing up the iota count. - ShellCompDirectiveDefault: 0, -}; - - - -export type Completion = { - action: string; - description?: string; -}; - -export type Callback = ( - previousArgs: string[], - toComplete: string, -) => Completion[] | Promise; - -export type Positional = { - required: boolean; - variadic: boolean; - completion: Callback; -}; - - -export const positionalMap = new Map(); - -export const flagMap = new Map(); diff --git a/src/bash.ts b/src/bash.ts new file mode 100644 index 0000000..7514d89 --- /dev/null +++ b/src/bash.ts @@ -0,0 +1 @@ +export function generate(name: string, exec: string) {} diff --git a/src/cac.ts b/src/cac.ts new file mode 100644 index 0000000..76add15 --- /dev/null +++ b/src/cac.ts @@ -0,0 +1,366 @@ +import * as zsh from "./zsh"; +import * as bash from "./bash"; +import * as fish from "./fish"; +import * as powershell from "./powershell"; +import type { CAC } from "cac"; +import { Completion } from "./"; + +const execPath = process.execPath; +const processArgs = process.argv.slice(1); +const quotedExecPath = quoteIfNeeded(execPath); +const quotedProcessArgs = processArgs.map(quoteIfNeeded); +const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded); + +const x = `${quotedExecPath} ${quotedProcessExecArgs.join(" ")} ${quotedProcessArgs[0]}`; + +function quoteIfNeeded(path: string): string { + return path.includes(" ") ? `'${path}'` : path; +} + +// export default function tab(instance: CAC): void { +// instance.command("complete [shell]").action(async (shell, extra) => { +// switch (shell) { +// case "zsh": { +// const script = zsh.generate(instance.name, x); +// console.log(script); +// break; +// } +// case "bash": { +// const script = bash.generate(instance.name, x); +// console.log(script); +// break; +// } +// case "fish": { +// const script = fish.generate(instance.name, x); +// console.log(script); +// break; +// } +// case "powershell": { +// const script = powershell.generate(instance.name, x); +// console.log(script); +// break; +// } +// default: { +// const args: string[] = extra["--"]; + +// instance.showHelpOnExit = false; +// let directive = ShellCompDirective.ShellCompDirectiveDefault; + +// const endsWithSpace = args[args.length - 1] === ""; +// if (endsWithSpace) { +// args.pop(); +// } + +// let toComplete = args[args.length - 1] || ""; +// const previousArgs = args.slice(0, -1); + +// const completions: string[] = []; + +// instance.unsetMatchedCommand(); +// instance.parse([execPath, processArgs[0], ...previousArgs], { +// run: false, +// }); + +// const command = instance.matchedCommand ?? instance.globalCommand; + +// const options = [ +// ...new Set([ +// ...(command?.options ?? []), +// ...instance.globalCommand.options, +// ]), +// ]; + +// let isCompletingFlagValue = false; +// let flagName = ""; +// let option: (typeof options)[number] | null = null; +// const lastArg = previousArgs[previousArgs.length - 1]; + +// function processOption() { +// const matchedOption = options.find((o) => +// o.names.some((name) => name === flagName) +// ); + +// if (matchedOption && !matchedOption.isBoolean) { +// isCompletingFlagValue = true; +// option = matchedOption; +// if (endsWithSpace) { +// toComplete = ""; +// } +// } else { +// isCompletingFlagValue = false; +// option = null; +// } +// } + +// if (toComplete.startsWith("--")) { +// // Long option +// flagName = toComplete.slice(2); +// const equalsIndex = flagName.indexOf("="); +// if (equalsIndex !== -1 && !endsWithSpace) { +// // Option with '=', get the name before '=' +// flagName = flagName.slice(0, equalsIndex); +// toComplete = toComplete.slice(toComplete.indexOf("=") + 1); +// processOption(); +// } else if (!endsWithSpace) { +// // If not ending with space, still typing option name +// flagName = ""; +// } else { +// // User pressed space after typing the option name +// processOption(); +// toComplete = ""; +// } +// } else if (toComplete.startsWith("-") && toComplete.length > 1) { +// // Short option +// flagName = toComplete.slice(1); +// if (!endsWithSpace) { +// // Still typing option name +// flagName = ""; +// } else { +// processOption(); +// toComplete = ""; +// } +// } else if (lastArg?.startsWith("--") && !endsWithSpace) { +// flagName = lastArg.slice(2); +// processOption(); +// } else if ( +// lastArg?.startsWith("-") && +// lastArg.length > 1 && +// !endsWithSpace +// ) { +// flagName = lastArg.slice(2); +// processOption(); +// } + +// if (isCompletingFlagValue) { +// const flagCompletionFn = flagMap.get( +// `${command.name} ${option?.name}` +// ); + +// if (flagCompletionFn) { +// // Call custom completion function for the flag +// const comps = await flagCompletionFn(previousArgs, toComplete); +// completions.push( +// ...comps.map( +// (comp) => `${comp.action}\t${comp.description ?? ""}` +// ) +// ); +// directive = ShellCompDirective.ShellCompDirectiveNoFileComp; +// } else { +// // Default completion (e.g., file completion) +// directive = ShellCompDirective.ShellCompDirectiveDefault; +// } +// } else if (toComplete.startsWith("-") && !endsWithSpace) { +// const flag = toComplete.replace(/^-+/, ""); // Remove leading '-' + +// // Determine options to suggest +// let optionsToSuggest = options.filter((o) => { +// const equalToDefault = +// "default" in o.config && +// instance.options[o.name] === o.config.default; +// return ( +// o.names.some((name) => name.startsWith(flag)) && +// !(instance.options[o.name] && !equalToDefault) +// ); +// }); + +// const requiredOptions = optionsToSuggest.filter((o) => o.required); + +// if (requiredOptions.length) { +// // Required options not yet specified +// optionsToSuggest = requiredOptions; +// } + +// if (optionsToSuggest.length > 0) { +// completions.push( +// ...optionsToSuggest.map( +// (o) => `--${o.name}\t${o.description ?? ""}` +// ) +// ); +// } + +// directive = ShellCompDirective.ShellCompDirectiveNoFileComp; +// } else { +// instance.parse( +// [execPath, processArgs[0], ...previousArgs, toComplete], +// { +// run: false, +// } +// ); +// const fullCommandName = args +// .filter((arg) => !arg.startsWith("-")) +// .join(" "); + +// for (const c of instance.commands) { +// if (c.name === "complete") { +// // avoid showing completions for the completion server +// continue; +// } +// const fullCommandParts = fullCommandName.split(" "); +// const commandParts: { type: "command"; value: string }[] = c.name +// .split(" ") +// .map((part) => ({ type: "command", value: part })); +// const args: { +// type: "positional"; +// position: number; +// value: Positional; +// }[] = +// positionalMap.get(c.name)?.map((arg, i) => ({ +// type: "positional", +// position: i, +// value: arg, +// })) ?? []; +// const parts = [...commandParts, ...args]; + +// for (let i = 0; i < parts.length; i++) { +// const fullCommandPart = fullCommandParts[i]; +// const part = parts[i]; + +// if (part.type === "command") { +// // Command part matching +// if (part.value === fullCommandPart) { +// // Command part matches user input, continue to next part +// continue; +// } else if ( +// !fullCommandPart || +// part.value.startsWith(fullCommandPart) +// ) { +// // User is typing this command part, provide completion +// completions.push(`${part.value}\t${c.description ?? ""}`); +// } +// // Command part does not match, break +// break; +// } else if (part.type === "positional") { +// const positional = part.value; +// // Positional argument handling +// if (part.value.variadic) { +// const comps = await positional.completion( +// previousArgs, +// toComplete +// ); +// completions.push( +// ...comps.map( +// (comp) => `${comp.action}\t${comp.description ?? ""}` +// ) +// ); +// break; +// } +// if (typeof fullCommandPart !== "undefined") { +// // User has provided input for this positional argument +// if (i === fullCommandParts.length - 1 && !endsWithSpace) { +// // User is still typing this positional argument, provide completions +// const comps = await positional.completion( +// previousArgs, +// toComplete +// ); +// completions.push( +// ...comps.map( +// (comp) => `${comp.action}\t${comp.description ?? ""}` +// ) +// ); +// break; +// } else { +// // Positional argument is already provided, move to next +// previousArgs.push(fullCommandPart); +// continue; +// } +// } else { +// // User has not provided input for this positional argument +// const comps = await positional.completion( +// previousArgs, +// toComplete +// ); +// completions.push( +// ...comps.map( +// (comp) => `${comp.action}\t${comp.description ?? ""}` +// ) +// ); +// break; +// } +// } +// } +// } +// } + +// // Output completions +// for (const comp of completions) { +// console.log(comp.split("\n")[0].trim()); +// } +// console.log(`:${directive}`); +// console.error(`Completion ended with directive: ${directive}`); +// } +// } +// }); +// } + +export default function tab(instance: CAC): Completion { + const completion = new Completion(); + + // Add all commands and their options + for (const cmd of [instance.globalCommand, ...instance.commands]) { + if (cmd.name === 'complete') continue; // Skip completion command + + // Get positional args info from command usage + const args = (cmd.rawName.match(/\[.*?\]|\<.*?\>/g) || []) + .map(arg => arg.startsWith('[')); // true if optional (wrapped in []) + + // Add command to completion + const commandName = completion.addCommand( + cmd.name === '@@global@@' ? '' : cmd.name, + cmd.description || '', + args, + async () => [] + ); + + // Add command options + for (const option of [...instance.globalCommand.options, ...cmd.options]) { + completion.addOption( + commandName, + `--${option.name}`, + option.description || '', + async () => [] + ); + } + } + + instance.command("complete [shell]").action(async (shell, extra) => { + switch (shell) { + case "zsh": { + const script = zsh.generate(instance.name, x); + console.log(script); + break; + } + case "bash": { + const script = bash.generate(instance.name, x); + console.log(script); + break; + } + case "fish": { + const script = fish.generate(instance.name, x); + console.log(script); + break; + } + case "powershell": { + const script = powershell.generate(instance.name, x); + console.log(script); + break; + } + default: { + const args: string[] = extra["--"]; + instance.showHelpOnExit = false; + + // Parse current command context + instance.unsetMatchedCommand(); + instance.parse([execPath, processArgs[0], ...args], { + run: false, + }); + + // const matchedCommand = instance.matchedCommand?.name || ''; + // const potentialCommand = args.join(' ') + // console.log(potentialCommand) + return completion.parse(args); + } + } + }); + + return completion; +} \ No newline at end of file diff --git a/src/citty.ts b/src/citty.ts new file mode 100644 index 0000000..07e1541 --- /dev/null +++ b/src/citty.ts @@ -0,0 +1,185 @@ +import { ArgDef, defineCommand, parseArgs } from 'citty'; +import * as zsh from './zsh'; +import * as bash from './bash'; +import * as fish from './fish'; +import * as powershell from './powershell'; +import { Completion } from '.'; +import type { + ArgsDef, + CommandDef, + PositionalArgDef, + SubCommandsDef, +} from 'citty'; + +function quoteIfNeeded(path: string) { + return path.includes(' ') ? `'${path}'` : path; +} + +const execPath = process.execPath; +const processArgs = process.argv.slice(1); +const quotedExecPath = quoteIfNeeded(execPath); +const quotedProcessArgs = processArgs.map(quoteIfNeeded); +const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded); +const x = `${quotedExecPath} ${quotedProcessExecArgs.join(' ')} ${quotedProcessArgs[0]}`; + +function isConfigPositional(config: CommandDef) { + return ( + config.args && + Object.values(config.args).some((arg) => arg.type === 'positional') + ); +} + +async function handleSubCommands( + completion: Completion, + subCommands: SubCommandsDef, + parentCmd?: string +) { + for (const [cmd, resolvableConfig] of Object.entries(subCommands)) { + const config = await resolve(resolvableConfig); + const meta = await resolve(config.meta); + const subCommands = await resolve(config.subCommands); + + if (!meta || typeof meta?.description !== 'string') { + throw new Error('Invalid meta or missing description.'); + } + const isPositional = isConfigPositional(config); + const name = completion.addCommand( + cmd, + meta.description, + isPositional ? [false] : [], + async (previousArgs, toComplete, endsWithSpace) => { + return []; + }, + parentCmd + ); + + // Handle nested subcommands recursively + if (subCommands) { + await handleSubCommands(completion, subCommands, name); + } + + // Handle arguments + if (config.args) { + for (const [argName, argConfig] of Object.entries(config.args)) { + const conf = argConfig as ArgDef; + if (conf.type === 'positional') { + continue; + } + completion.addOption( + name, + `--${argName}`, + conf.description ?? '', + async (previousArgs, toComplete, endsWithSpace) => { + return []; + } + ); + } + } + } +} + +export default async function tab( + instance: CommandDef +) { + const completion = new Completion(); + + const meta = await resolve(instance.meta); + + if (!meta) { + throw new Error('Invalid meta.'); + } + const name = meta.name; + if (!name) { + throw new Error('Invalid meta or missing name.'); + } + + const subCommands = await resolve(instance.subCommands); + if (!subCommands) { + throw new Error('Invalid or missing subCommands.'); + } + + const root = ''; + const isPositional = isConfigPositional(instance); + completion.addCommand( + root, + meta?.description ?? '', + isPositional ? [false] : [], + async (previousArgs, toComplete, endsWithSpace) => { + return []; + } + ); + + await handleSubCommands(completion, subCommands); + + if (instance.args) { + for (const [argName, argConfig] of Object.entries(instance.args)) { + const conf = argConfig as PositionalArgDef; + completion.addOption( + root, + `--${argName}`, + conf.description ?? '', + async (previousArgs, toComplete, endsWithSpace) => { + return []; + } + ); + } + } + + const completeCommand = defineCommand({ + meta: { + name: 'complete', + description: 'Generate shell completion scripts', + }, + async run(ctx) { + let shell: string | undefined = ctx.rawArgs[0]; + const extra = ctx.rawArgs.slice(ctx.rawArgs.indexOf('--') + 1); + + if (shell === '--') { + shell = undefined; + } + + switch (shell) { + case 'zsh': { + const script = zsh.generate(name, x); + console.log(script); + break; + } + case 'bash': { + const script = bash.generate(name, x); + console.log(script); + break; + } + case 'fish': { + const script = fish.generate(name, x); + console.log(script); + break; + } + case 'powershell': { + const script = powershell.generate(name, x); + console.log(script); + break; + } + default: { + const args = (await resolve(instance.args))!; + const parsed = parseArgs(extra, args); + // TODO: this is not ideal at all + const matchedCommand = parsed._.join(' ').trim(); + // TODO: `command lint i` does not work because `lint` and `i` are potential commands + // instead the potential command should only be `lint` + // and `i` is the to be completed part + return completion.parse(extra, matchedCommand); + } + } + }, + }); + + subCommands.complete = completeCommand; + + return completion; +} + +type Resolvable = T | Promise | (() => T) | (() => Promise); + +async function resolve(resolvable: Resolvable): Promise { + return resolvable instanceof Function ? await resolvable() : await resolvable; +} diff --git a/src/fish.ts b/src/fish.ts new file mode 100644 index 0000000..7514d89 --- /dev/null +++ b/src/fish.ts @@ -0,0 +1 @@ +export function generate(name: string, exec: string) {} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..cc789a1 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,361 @@ +import * as zsh from './zsh'; +import * as bash from './bash'; +import * as fish from './fish'; +import * as powershell from './powershell'; + +// ShellCompRequestCmd is the name of the hidden command that is used to request +// completion results from the program. It is used by the shell completion scripts. +export const ShellCompRequestCmd: string = '__complete'; + +// ShellCompNoDescRequestCmd is the name of the hidden command that is used to request +// completion results without their description. It is used by the shell completion scripts. +export const ShellCompNoDescRequestCmd: string = '__completeNoDesc'; + +// ShellCompDirective is a bit map representing the different behaviors the shell +// can be instructed to have once completions have been provided. +export const ShellCompDirective = { + // ShellCompDirectiveError indicates an error occurred and completions should be ignored. + ShellCompDirectiveError: 1 << 0, + + // ShellCompDirectiveNoSpace indicates that the shell should not add a space + // after the completion even if there is a single completion provided. + ShellCompDirectiveNoSpace: 1 << 1, + + // ShellCompDirectiveNoFileComp indicates that the shell should not provide + // file completion even when no completion is provided. + ShellCompDirectiveNoFileComp: 1 << 2, + + // ShellCompDirectiveFilterFileExt indicates that the provided completions + // should be used as file extension filters. + // For flags, using Command.MarkFlagFilename() and Command.MarkPersistentFlagFilename() + // is a shortcut to using this directive explicitly. The BashCompFilenameExt + // annotation can also be used to obtain the same behavior for flags. + ShellCompDirectiveFilterFileExt: 1 << 3, + + // ShellCompDirectiveFilterDirs indicates that only directory names should + // be provided in file completion. To request directory names within another + // directory, the returned completions should specify the directory within + // which to search. The BashCompSubdirsInDir annotation can be used to + // obtain the same behavior but only for flags. + ShellCompDirectiveFilterDirs: 1 << 4, + + // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order + // in which the completions are provided. + ShellCompDirectiveKeepOrder: 1 << 5, + + // =========================================================================== + + // All directives using iota (or equivalent in Go) should be above this one. + // For internal use. + shellCompDirectiveMaxValue: 1 << 6, + + // ShellCompDirectiveDefault indicates to let the shell perform its default + // behavior after completions have been provided. + // This one must be last to avoid messing up the iota count. + ShellCompDirectiveDefault: 0, +}; + +export type Positional = { + required: boolean; + variadic: boolean; + completion: Handler; +}; + +type Item = { + description: string; + value: string; +}; + +type Handler = ( + previousArgs: string[], + toComplete: string, + endsWithSpace: boolean +) => Item[] | Promise; + +type Option = { + description: string; + handler: Handler; +}; + +type Command = { + name: string; + description: string; + args: boolean[] + handler: Handler; + options: Map; + parent?: Command; +}; + +export class Completion { + commands = new Map(); + completions: Item[] = [] + directive = ShellCompDirective.ShellCompDirectiveDefault; + + // vite [...files] + // args: [false, false, true], only the last argument can be variadic + addCommand( + name: string, + description: string, + args: boolean[], + handler: Handler, + parent?: string + ) { + const key = parent ? `${parent} ${name}` : name; + this.commands.set(key, { + name: key, + description, + args, + handler, + options: new Map(), + parent: parent ? this.commands.get(parent)! : undefined, + }); + return key; + } + + // --port + addOption( + command: string, + option: string, + description: string, + handler: Handler + ) { + const cmd = this.commands.get(command); + if (!cmd) { + throw new Error(`Command ${command} not found.`); + } + cmd.options.set(option, { description, handler }); + return option; + } + + // TODO: this should be aware of boolean args and stuff + private stripOptions(args: string[]): string[] { + const parts: string[] = [] + let option = false; + for (const k of args) { + if (k.startsWith('-')) { + option = true; + continue + } + if (option) { + option = false; + continue + } + parts.push(k) + } + return parts + } + + private matchCommand(args: string[]): [Command, string[]] { + args = this.stripOptions(args); + let parts: string[] = []; + let remaining: string[] = []; + let matched: Command = this.commands.get('')! + for (let i = 0; i < args.length; i++) { + const k = args[i]; + parts.push(k); + const potential = this.commands.get(parts.join(' ')) + + if (potential) { + matched = potential; + } else { + remaining = args.slice(i, args.length); + break + } + } + return [matched, remaining]; + } + + async parse(args: string[]) { + const endsWithSpace = args[args.length - 1] === ''; + + if (endsWithSpace) { + args.pop(); + } + + let toComplete = args[args.length - 1] || ''; + const previousArgs = args.slice(0, -1); + + if (endsWithSpace) { + previousArgs.push(toComplete); + toComplete = ''; + } + + const [matchedCommand, remaining] = this.matchCommand(previousArgs); + + const lastPrevArg = previousArgs[previousArgs.length - 1]; + + // 1. Handle flag/option completion + if (this.shouldCompleteFlags(lastPrevArg, toComplete, endsWithSpace)) { + await this.handleFlagCompletion( + matchedCommand, + previousArgs, + toComplete, + endsWithSpace, + lastPrevArg, + ); + } + else { + // 2. Handle command/subcommand completion + if (this.shouldCompleteCommands(toComplete, endsWithSpace)) { + await this.handleCommandCompletion( + previousArgs, + toComplete, + ); + } + // 3. Handle positional arguments + if (matchedCommand && matchedCommand.args.length > 0) { + await this.handlePositionalCompletion( + matchedCommand, + previousArgs, + toComplete, + endsWithSpace, + ); + } + } + this.complete(toComplete) + } + + private complete(toComplete: string) { + this.directive = ShellCompDirective.ShellCompDirectiveNoFileComp; + + const seen = new Set(); + this.completions + .filter((comp) => { + if (seen.has(comp.value)) return false; + seen.add(comp.value); + return true; + }) + .filter((comp) => comp.value.startsWith(toComplete)) + .forEach((comp) => console.log(`${comp.value}\t${comp.description ?? ''}`)); + console.log(`:${this.directive}`); + } + + private shouldCompleteFlags(lastPrevArg: string | undefined, toComplete: string, endsWithSpace: boolean): boolean { + return (lastPrevArg?.startsWith('--')) || toComplete.startsWith('--'); + } + + private shouldCompleteCommands(toComplete: string, endsWithSpace: boolean): boolean { + return !toComplete.startsWith('-'); + } + + private async handleFlagCompletion( + command: Command, + previousArgs: string[], + toComplete: string, + endsWithSpace: boolean, + lastPrevArg: string | undefined, + ) { + // Handle flag value completion + let flagName: string | undefined; + let valueToComplete = toComplete; + + if (toComplete.includes('=')) { + // Handle --flag=value case + const parts = toComplete.split('='); + flagName = parts[0]; + valueToComplete = parts[1] || ''; + } else if (lastPrevArg?.startsWith('--')) { + // Handle --flag value case + flagName = lastPrevArg; + } + + if (flagName) { + const option = command.options.get(flagName); + if (option) { + const suggestions = await option.handler(previousArgs, valueToComplete, endsWithSpace); + if (toComplete.includes('=')) { + // Reconstruct the full flag=value format + this.completions = suggestions.map(suggestion => ({ + value: `${flagName}=${suggestion.value}`, + description: suggestion.description + })); + } else { + this.completions.push(...suggestions); + } + } + return; + } + + // Handle flag name completion + if (toComplete.startsWith('--')) { + for (const [name, option] of command.options) { + if (name.startsWith(toComplete)) { + this.completions.push({ + value: name, + description: option.description + }); + } + } + } + } + + private async handleCommandCompletion( + previousArgs: string[], + toComplete: string, + ) { + const commandParts = [...previousArgs]; + + for (const k of this.commands.keys()) { + if (k === '') continue; + const parts = k.split(' '); + let match = true; + + let i = 0; + while (i < commandParts.length) { + if (parts[i] !== commandParts[i]) { + match = false; + break + } + i++; + } + if (match && parts[i]?.startsWith(toComplete)) { + this.completions.push({ + value: parts[i], + description: this.commands.get(k)!.description + }); + } + } + } + + private async handlePositionalCompletion( + command: Command, + previousArgs: string[], + toComplete: string, + endsWithSpace: boolean, + ) { + const suggestions = await command.handler(previousArgs, toComplete, endsWithSpace); + this.completions.push(...suggestions); + } +} + +export function script( + shell: 'zsh' | 'bash' | 'fish' | 'powershell', + name: string, + x: string +) { + switch (shell) { + case 'zsh': { + const script = zsh.generate(name, x); + console.log(script); + break; + } + case 'bash': { + const script = bash.generate(name, x); + console.log(script); + break; + } + case 'fish': { + const script = fish.generate(name, x); + console.log(script); + break; + } + case 'powershell': { + const script = powershell.generate(name, x); + console.log(script); + break; + } + default: { + throw new Error(`Unsupported shell: ${shell}`); + } + } +} diff --git a/powershell.ts b/src/powershell.ts similarity index 96% rename from powershell.ts rename to src/powershell.ts index 5dc70b4..ffc7f4b 100644 --- a/powershell.ts +++ b/src/powershell.ts @@ -1,275 +1,275 @@ -import { ShellCompDirective } from "./shared"; - -// TODO: issue with -- -- completions - -export function generate( - name: string, - exec: string, - includeDesc = false -): string { - // Replace '-' and ':' with '_' for variable names - const nameForVar = name.replace(/[-:]/g, "_"); - - // Determine the completion command - // const compCmd = includeDesc ? "complete" : "complete"; - - // Shell completion directives - const ShellCompDirectiveError = ShellCompDirective.ShellCompDirectiveError; - const ShellCompDirectiveNoSpace = - ShellCompDirective.ShellCompDirectiveNoSpace; - const ShellCompDirectiveNoFileComp = - ShellCompDirective.ShellCompDirectiveNoFileComp; - const ShellCompDirectiveFilterFileExt = - ShellCompDirective.ShellCompDirectiveFilterFileExt; - const ShellCompDirectiveFilterDirs = - ShellCompDirective.ShellCompDirectiveFilterDirs; - const ShellCompDirectiveKeepOrder = - ShellCompDirective.ShellCompDirectiveKeepOrder; - - return `# powershell completion for ${name} -*- shell-script -*- - - [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 - function __${name}_debug { - if ($env:BASH_COMP_DEBUG_FILE) { - "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE" - } - } - - filter __${name}_escapeStringWithSpecialChars { - $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|"|\\||<|>|&','\`$&' - } - -[scriptblock]$__${nameForVar}CompleterBlock = { - param( - $WordToComplete, - $CommandAst, - $CursorPosition - ) - - # Get the current command line and convert into a string - $Command = $CommandAst.CommandElements - $Command = "$Command" - - __${name}_debug "" - __${name}_debug "========= starting completion logic ==========" - __${name}_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition" - - # The user could have moved the cursor backwards on the command-line. - # We need to trigger completion from the $CursorPosition location, so we need - # to truncate the command-line ($Command) up to the $CursorPosition location. - # Make sure the $Command is longer then the $CursorPosition before we truncate. - # This happens because the $Command does not include the last space. - if ($Command.Length -gt $CursorPosition) { - $Command = $Command.Substring(0, $CursorPosition) - } - __${name}_debug "Truncated command: $Command" - - $ShellCompDirectiveError=${ShellCompDirectiveError} - $ShellCompDirectiveNoSpace=${ShellCompDirectiveNoSpace} - $ShellCompDirectiveNoFileComp=${ShellCompDirectiveNoFileComp} - $ShellCompDirectiveFilterFileExt=${ShellCompDirectiveFilterFileExt} - $ShellCompDirectiveFilterDirs=${ShellCompDirectiveFilterDirs} - $ShellCompDirectiveKeepOrder=${ShellCompDirectiveKeepOrder} - - # Prepare the command to request completions for the program. - # Split the command at the first space to separate the program and arguments. - $Program, $Arguments = $Command.Split(" ", 2) - - $RequestComp = "& ${exec} complete -- $Arguments" - __${name}_debug "RequestComp: $RequestComp" - - # we cannot use $WordToComplete because it - # has the wrong values if the cursor was moved - # so use the last argument - if ($WordToComplete -ne "" ) { - $WordToComplete = $Arguments.Split(" ")[-1] - } - __${name}_debug "New WordToComplete: $WordToComplete" - - - # Check for flag with equal sign - $IsEqualFlag = ($WordToComplete -Like "--*=*" ) - if ( $IsEqualFlag ) { - __${name}_debug "Completing equal sign flag" - # Remove the flag part - $Flag, $WordToComplete = $WordToComplete.Split("=", 2) - } - - if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) { - # If the last parameter is complete (there is a space following it) - # We add an extra empty parameter so we can indicate this to the go method. - __${name}_debug "Adding extra empty parameter" - # PowerShell 7.2+ changed the way how the arguments are passed to executables, - # so for pre-7.2 or when Legacy argument passing is enabled we need to use - if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or - ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or - (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and - $PSNativeCommandArgumentPassing -eq 'Legacy')) { - $RequestComp="$RequestComp" + ' \`"\`"' - } else { - $RequestComp = "$RequestComp" + ' ""' - } - } - - __${name}_debug "Calling $RequestComp" - # First disable ActiveHelp which is not supported for Powershell - $env:ActiveHelp = 0 - - # call the command store the output in $out and redirect stderr and stdout to null - # $Out is an array contains each line per element - Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null - - # get directive from last line - [int]$Directive = $Out[-1].TrimStart(':') - if ($Directive -eq "") { - # There is no directive specified - $Directive = 0 - } - __${name}_debug "The completion directive is: $Directive" - - # remove directive (last element) from out - $Out = $Out | Where-Object { $_ -ne $Out[-1] } - __${name}_debug "The completions are: $Out" - - if (($Directive -band $ShellCompDirectiveError) -ne 0 ) { - # Error code. No completion. - __${name}_debug "Received error from custom completion go code" - return - } - - $Longest = 0 - [Array]$Values = $Out | ForEach-Object { - # Split the output in name and description - $Name, $Description = $_.Split("\`t", 2) - __${name}_debug "Name: $Name Description: $Description" - - # Look for the longest completion so that we can format things nicely - if ($Longest -lt $Name.Length) { - $Longest = $Name.Length - } - - # Set the description to a one space string if there is none set. - # This is needed because the CompletionResult does not accept an empty string as argument - if (-Not $Description) { - $Description = " " - } - @{ Name = "$Name"; Description = "$Description" } - } - - - $Space = " " - if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) { - # remove the space here - __${name}_debug "ShellCompDirectiveNoSpace is called" - $Space = "" - } - - if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or - (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { - __${name}_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" - - # return here to prevent the completion of the extensions - return - } - - $Values = $Values | Where-Object { - # filter the result - $_.Name -like "$WordToComplete*" - - # Join the flag back if we have an equal sign flag - if ( $IsEqualFlag ) { - __${name}_debug "Join the equal sign flag back to the completion value" - $_.Name = $Flag + "=" + $_.Name - } - } - - # we sort the values in ascending order by name if keep order isn't passed - if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { - $Values = $Values | Sort-Object -Property Name - } - - if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { - __${name}_debug "ShellCompDirectiveNoFileComp is called" - - if ($Values.Length -eq 0) { - # Just print an empty string here so the - # shell does not start to complete paths. - # We cannot use CompletionResult here because - # it does not accept an empty string as argument. - "" - return - } - } - - # Get the current mode - $Mode = (Get-PSReadLineKeyHandler | Where-Object { $_.Key -eq "Tab" }).Function - __${name}_debug "Mode: $Mode" - - $Values | ForEach-Object { - - # store temporary because switch will overwrite $_ - $comp = $_ - - # PowerShell supports three different completion modes - # - TabCompleteNext (default windows style - on each key press the next option is displayed) - # - Complete (works like bash) - # - MenuComplete (works like zsh) - # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function - - # CompletionResult Arguments: - # 1) CompletionText text to be used as the auto completion result - # 2) ListItemText text to be displayed in the suggestion list - # 3) ResultType type of completion result - # 4) ToolTip text for the tooltip with details about the object - - switch ($Mode) { - - # bash like - "Complete" { - - if ($Values.Length -eq 1) { - __${name}_debug "Only one completion left" - - # insert space after value - [System.Management.Automation.CompletionResult]::new($($comp.Name | __${name}_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") - - } else { - # Add the proper number of spaces to align the descriptions - while($comp.Name.Length -lt $Longest) { - $comp.Name = $comp.Name + " " - } - - # Check for empty description and only add parentheses if needed - if ($($comp.Description) -eq " " ) { - $Description = "" - } else { - $Description = " ($($comp.Description))" - } - - [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") - } - } - - # zsh like - "MenuComplete" { - # insert space after value - # MenuComplete will automatically show the ToolTip of - # the highlighted value at the bottom of the suggestions. - [System.Management.Automation.CompletionResult]::new($($comp.Name | __${name}_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") - } - - # TabCompleteNext and in case we get something unknown - Default { - # Like MenuComplete but we don't want to add a space here because - # the user need to press space anyway to get the completion. - # Description will not be shown because that's not possible with TabCompleteNext - [System.Management.Automation.CompletionResult]::new($($comp.Name | __${name}_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") - } - } - - } -} - -Register-ArgumentCompleter -CommandName '${name}' -ScriptBlock $__${nameForVar}CompleterBlock -`; -} +import { ShellCompDirective } from './'; + +// TODO: issue with -- -- completions + +export function generate( + name: string, + exec: string, + includeDesc = false +): string { + // Replace '-' and ':' with '_' for variable names + const nameForVar = name.replace(/[-:]/g, '_'); + + // Determine the completion command + // const compCmd = includeDesc ? "complete" : "complete"; + + // Shell completion directives + const ShellCompDirectiveError = ShellCompDirective.ShellCompDirectiveError; + const ShellCompDirectiveNoSpace = + ShellCompDirective.ShellCompDirectiveNoSpace; + const ShellCompDirectiveNoFileComp = + ShellCompDirective.ShellCompDirectiveNoFileComp; + const ShellCompDirectiveFilterFileExt = + ShellCompDirective.ShellCompDirectiveFilterFileExt; + const ShellCompDirectiveFilterDirs = + ShellCompDirective.ShellCompDirectiveFilterDirs; + const ShellCompDirectiveKeepOrder = + ShellCompDirective.ShellCompDirectiveKeepOrder; + + return `# powershell completion for ${name} -*- shell-script -*- + + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + function __${name}_debug { + if ($env:BASH_COMP_DEBUG_FILE) { + "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE" + } + } + + filter __${name}_escapeStringWithSpecialChars { + $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|"|\\||<|>|&','\`$&' + } + +[scriptblock]$__${nameForVar}CompleterBlock = { + param( + $WordToComplete, + $CommandAst, + $CursorPosition + ) + + # Get the current command line and convert into a string + $Command = $CommandAst.CommandElements + $Command = "$Command" + + __${name}_debug "" + __${name}_debug "========= starting completion logic ==========" + __${name}_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition" + + # The user could have moved the cursor backwards on the command-line. + # We need to trigger completion from the $CursorPosition location, so we need + # to truncate the command-line ($Command) up to the $CursorPosition location. + # Make sure the $Command is longer then the $CursorPosition before we truncate. + # This happens because the $Command does not include the last space. + if ($Command.Length -gt $CursorPosition) { + $Command = $Command.Substring(0, $CursorPosition) + } + __${name}_debug "Truncated command: $Command" + + $ShellCompDirectiveError=${ShellCompDirectiveError} + $ShellCompDirectiveNoSpace=${ShellCompDirectiveNoSpace} + $ShellCompDirectiveNoFileComp=${ShellCompDirectiveNoFileComp} + $ShellCompDirectiveFilterFileExt=${ShellCompDirectiveFilterFileExt} + $ShellCompDirectiveFilterDirs=${ShellCompDirectiveFilterDirs} + $ShellCompDirectiveKeepOrder=${ShellCompDirectiveKeepOrder} + + # Prepare the command to request completions for the program. + # Split the command at the first space to separate the program and arguments. + $Program, $Arguments = $Command.Split(" ", 2) + + $RequestComp = "& ${exec} complete -- $Arguments" + __${name}_debug "RequestComp: $RequestComp" + + # we cannot use $WordToComplete because it + # has the wrong values if the cursor was moved + # so use the last argument + if ($WordToComplete -ne "" ) { + $WordToComplete = $Arguments.Split(" ")[-1] + } + __${name}_debug "New WordToComplete: $WordToComplete" + + + # Check for flag with equal sign + $IsEqualFlag = ($WordToComplete -Like "--*=*" ) + if ( $IsEqualFlag ) { + __${name}_debug "Completing equal sign flag" + # Remove the flag part + $Flag, $WordToComplete = $WordToComplete.Split("=", 2) + } + + if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) { + # If the last parameter is complete (there is a space following it) + # We add an extra empty parameter so we can indicate this to the go method. + __${name}_debug "Adding extra empty parameter" + # PowerShell 7.2+ changed the way how the arguments are passed to executables, + # so for pre-7.2 or when Legacy argument passing is enabled we need to use + if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or + ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or + (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and + $PSNativeCommandArgumentPassing -eq 'Legacy')) { + $RequestComp="$RequestComp" + ' \`"\`"' + } else { + $RequestComp = "$RequestComp" + ' ""' + } + } + + __${name}_debug "Calling $RequestComp" + # First disable ActiveHelp which is not supported for Powershell + $env:ActiveHelp = 0 + + # call the command store the output in $out and redirect stderr and stdout to null + # $Out is an array contains each line per element + Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null + + # get directive from last line + [int]$Directive = $Out[-1].TrimStart(':') + if ($Directive -eq "") { + # There is no directive specified + $Directive = 0 + } + __${name}_debug "The completion directive is: $Directive" + + # remove directive (last element) from out + $Out = $Out | Where-Object { $_ -ne $Out[-1] } + __${name}_debug "The completions are: $Out" + + if (($Directive -band $ShellCompDirectiveError) -ne 0 ) { + # Error code. No completion. + __${name}_debug "Received error from custom completion go code" + return + } + + $Longest = 0 + [Array]$Values = $Out | ForEach-Object { + # Split the output in name and description + $Name, $Description = $_.Split("\`t", 2) + __${name}_debug "Name: $Name Description: $Description" + + # Look for the longest completion so that we can format things nicely + if ($Longest -lt $Name.Length) { + $Longest = $Name.Length + } + + # Set the description to a one space string if there is none set. + # This is needed because the CompletionResult does not accept an empty string as argument + if (-Not $Description) { + $Description = " " + } + @{ Name = "$Name"; Description = "$Description" } + } + + + $Space = " " + if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) { + # remove the space here + __${name}_debug "ShellCompDirectiveNoSpace is called" + $Space = "" + } + + if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or + (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { + __${name}_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" + + # return here to prevent the completion of the extensions + return + } + + $Values = $Values | Where-Object { + # filter the result + $_.Name -like "$WordToComplete*" + + # Join the flag back if we have an equal sign flag + if ( $IsEqualFlag ) { + __${name}_debug "Join the equal sign flag back to the completion value" + $_.Name = $Flag + "=" + $_.Name + } + } + + # we sort the values in ascending order by name if keep order isn't passed + if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { + $Values = $Values | Sort-Object -Property Name + } + + if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { + __${name}_debug "ShellCompDirectiveNoFileComp is called" + + if ($Values.Length -eq 0) { + # Just print an empty string here so the + # shell does not start to complete paths. + # We cannot use CompletionResult here because + # it does not accept an empty string as argument. + "" + return + } + } + + # Get the current mode + $Mode = (Get-PSReadLineKeyHandler | Where-Object { $_.Key -eq "Tab" }).Function + __${name}_debug "Mode: $Mode" + + $Values | ForEach-Object { + + # store temporary because switch will overwrite $_ + $comp = $_ + + # PowerShell supports three different completion modes + # - TabCompleteNext (default windows style - on each key press the next option is displayed) + # - Complete (works like bash) + # - MenuComplete (works like zsh) + # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function + + # CompletionResult Arguments: + # 1) CompletionText text to be used as the auto completion result + # 2) ListItemText text to be displayed in the suggestion list + # 3) ResultType type of completion result + # 4) ToolTip text for the tooltip with details about the object + + switch ($Mode) { + + # bash like + "Complete" { + + if ($Values.Length -eq 1) { + __${name}_debug "Only one completion left" + + # insert space after value + [System.Management.Automation.CompletionResult]::new($($comp.Name | __${name}_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + + } else { + # Add the proper number of spaces to align the descriptions + while($comp.Name.Length -lt $Longest) { + $comp.Name = $comp.Name + " " + } + + # Check for empty description and only add parentheses if needed + if ($($comp.Description) -eq " " ) { + $Description = "" + } else { + $Description = " ($($comp.Description))" + } + + [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") + } + } + + # zsh like + "MenuComplete" { + # insert space after value + # MenuComplete will automatically show the ToolTip of + # the highlighted value at the bottom of the suggestions. + [System.Management.Automation.CompletionResult]::new($($comp.Name | __${name}_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } + + # TabCompleteNext and in case we get something unknown + Default { + # Like MenuComplete but we don't want to add a space here because + # the user need to press space anyway to get the completion. + # Description will not be shown because that's not possible with TabCompleteNext + [System.Management.Automation.CompletionResult]::new($($comp.Name | __${name}_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } + } + + } +} + +Register-ArgumentCompleter -CommandName '${name}' -ScriptBlock $__${nameForVar}CompleterBlock +`; +} diff --git a/src/shared.ts b/src/shared.ts new file mode 100644 index 0000000..e69de29 diff --git a/zsh.ts b/src/zsh.ts similarity index 99% rename from zsh.ts rename to src/zsh.ts index e7bcc5e..3dbdfb4 100644 --- a/zsh.ts +++ b/src/zsh.ts @@ -1,4 +1,4 @@ -import { ShellCompDirective } from "./shared"; +import { ShellCompDirective } from './'; export function generate(name: string, exec: string) { return `#compdef ${name} @@ -212,4 +212,3 @@ if [ "\${funcstack[1]}" = "_${name}" ]; then fi `; } - diff --git a/tests/__snapshots__/cli.test.ts.snap b/tests/__snapshots__/cli.test.ts.snap new file mode 100644 index 0000000..b5085c8 --- /dev/null +++ b/tests/__snapshots__/cli.test.ts.snap @@ -0,0 +1,143 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`cli completion tests for cac > cli option completion tests > should complete option for partial input '{ partial: '--p', expected: '--port' }' 1`] = ` +"--port Specify port +:4 +" +`; + +exports[`cli completion tests for cac > cli option exclusion tests > should not suggest already specified option '{ specified: '--config', shouldNotContain: '--config' }' 1`] = ` +":4 +" +`; + +exports[`cli completion tests for cac > cli option value handling > should handle unknown options with no completions 1`] = `":4"`; + +exports[`cli completion tests for cac > cli option value handling > should not show duplicate options 1`] = ` +"--config Use specified config file +--mode Set env mode +--logLevel info | warn | error | silent +:4 +" +`; + +exports[`cli completion tests for cac > cli option value handling > should resolve config option values correctly 1`] = ` +"vite.config.ts Vite config file +vite.config.js Vite config file +:4 +" +`; + +exports[`cli completion tests for cac > cli option value handling > should resolve port value correctly 1`] = ` +"--port=3000 Development server port +:4 +" +`; + +exports[`cli completion tests for cac > edge case completions for end with space > should keep suggesting the --port option if user typed partial but didn't end with space 1`] = ` +"--port Specify port +:4 +" +`; + +exports[`cli completion tests for cac > edge case completions for end with space > should suggest port values if user ends with space after \`--port\` 1`] = ` +"3000 Development server port +8080 Alternative port +:4 +" +`; + +exports[`cli completion tests for cac > edge case completions for end with space > should suggest port values if user typed \`--port=\` and hasn't typed a space or value yet 1`] = ` +"--port=3000 Development server port +--port=8080 Alternative port +:4 +" +`; + +exports[`cli completion tests for cac > positional argument completions > should complete multiple positional arguments when ending with part of the value 1`] = ` +"index.ts Index file +:4 +" +`; + +exports[`cli completion tests for cac > positional argument completions > should complete multiple positional arguments when ending with space 1`] = ` +"main.ts Main file +index.ts Index file +:4 +" +`; + +exports[`cli completion tests for cac > positional argument completions > should complete single positional argument when ending with space 1`] = ` +"main.ts Main file +index.ts Index file +:4 +" +`; + +exports[`cli completion tests for cac > should complete cli options 1`] = ` +"dev Start dev server +lint Lint project +:4 +" +`; + +exports[`cli completion tests for citty > cli option completion tests > should complete option for partial input '{ partial: '--p', expected: '--port' }' 1`] = ` +"--port Specify port +:4 +" +`; + +exports[`cli completion tests for citty > cli option exclusion tests > should not suggest already specified option '{ specified: '--config', shouldNotContain: '--config' }' 1`] = ` +":4 +" +`; + +exports[`cli completion tests for citty > cli option value handling > should handle unknown options with no completions 1`] = `":4"`; + +exports[`cli completion tests for citty > cli option value handling > should not show duplicate options 1`] = ` +"--config Use specified config file +--mode Set env mode +--logLevel info | warn | error | silent +:4 +" +`; + +exports[`cli completion tests for citty > cli option value handling > should resolve config option values correctly 1`] = ` +"vite.config.ts Vite config file +vite.config.js Vite config file +:4 +" +`; + +exports[`cli completion tests for citty > cli option value handling > should resolve port value correctly 1`] = ` +"--port=3000 Development server port +:4 +" +`; + +exports[`cli completion tests for citty > edge case completions for end with space > should keep suggesting the --port option if user typed partial but didn't end with space 1`] = ` +"--port Specify port +:4 +" +`; + +exports[`cli completion tests for citty > edge case completions for end with space > should suggest port values if user ends with space after \`--port\` 1`] = ` +"3000 Development server port +8080 Alternative port +:4 +" +`; + +exports[`cli completion tests for citty > edge case completions for end with space > should suggest port values if user typed \`--port=\` and hasn't typed a space or value yet 1`] = ` +"--port=3000 Development server port +--port=8080 Alternative port +:4 +" +`; + +exports[`cli completion tests for citty > should complete cli options 1`] = ` +"dev Start dev server +lint Lint project +:4 +" +`; diff --git a/tests/cli.test.ts b/tests/cli.test.ts index 412190e..1ba239e 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -1,106 +1,140 @@ -import { exec } from "child_process"; -import { describe, it, expect, test } from "vitest"; - -function runCommand(command: string): Promise { - return new Promise((resolve, reject) => { - exec(command, (error, stdout, stderr) => { - if (error) { - reject(stderr); - } else { - resolve(stdout); - } - }); - }); -} - -const cliTools = ["cac", "citty"]; - -describe.each(cliTools)("cli completion tests for %s", (cliTool) => { - const commandPrefix = `pnpm tsx demo.${cliTool}.ts complete --`; - - // it("should complete vite commands", async () => { - // const output = await runCommand(commandPrefix); - // console.log(`[${cliTool}] Command Output:`, output); - // expect(output).toContain("src/"); - // expect(output).toContain("./"); - // // expect(output).toContain('--base'); - // }); - - it("should complete cli options", async () => { - const output = await runCommand(`${commandPrefix} --`); - console.log(`[${cliTool}] Command Output:`, output); - expect(output).toContain("--port"); - expect(output).toContain("--config"); - expect(output).toContain("--base"); - expect(output).toContain("--logLevel"); - expect(output).toContain("--filter"); - expect(output).toContain("--mode"); - }); - - describe("cli option completion tests", () => { - const optionTests = [ - { partial: "--p", expected: "--port" }, - ]; - - test.each(optionTests)( - "should complete option for partial input '%s'", - async ({ partial, expected }) => { - const command = `${commandPrefix} ${partial}`; - const output = await runCommand(command); - console.log(`[${cliTool}] Complete ${partial} Output:`, output); - expect(output).toContain(expected); - } - ); - }); - - describe("cli option exclusion tests", () => { - const alreadySpecifiedTests = [ - { specified: "--port", shouldNotContain: "--port" }, - ]; - - test.each(alreadySpecifiedTests)( - "should not suggest already specified option '%s'", - async ({ specified, shouldNotContain }) => { - const command = `${commandPrefix} ${specified} --`; - const output = await runCommand(command); - console.log(`[${cliTool}] Already Specified ${specified} Output:`, output); - expect(output).not.toContain(shouldNotContain); - // expect(output).toContain("--base"); - } - ); - }); - - describe("cli option value handling", () => { - - it("should resolve port value correctly", async () => { - const command = `${commandPrefix} --port 3`; - const output = await runCommand(command); - console.log(`[${cliTool}] Port Value Output:`, output); - expect(output).toContain("3000"); - }); - - it("should handle conflicting options appropriately", async () => { - const command = `${commandPrefix} --port 3000 --`; - const output = await runCommand(command); - console.log(`[${cliTool}] Conflicting Options Output:`, output); - expect(output).not.toContain("--port"); - expect(output).toContain("--config"); - // expect(output).toContain("--base"); - }); - - it("should resolve config option values correctly", async () => { - const command = `${commandPrefix} --port 3000 --config vite.config`; - const output = await runCommand(command); - console.log(`[${cliTool}] Config Option Output:`, output); - expect(output).toContain("vite.config.ts"); - expect(output).toContain("vite.config.js"); - }); - - it("should handle unknown options with no completions", async () => { - const command = `${commandPrefix} --unknownoption`; - const output = await runCommand(command); - console.log(`[${cliTool}] No Completion Available Output:`, output); - expect(output.trim()).toMatch(/^(:\d+)?$/); - }); - }); -}); +import { exec } from 'child_process'; +import { describe, it, expect, test } from 'vitest'; + +function runCommand(command: string): Promise { + return new Promise((resolve, reject) => { + exec(command, (error, stdout, stderr) => { + if (error) { + reject(stderr); + } else { + resolve(stdout); + } + }); + }); +} + +const cliTools = ['citty', 'cac']; +// const cliTools = ['citty', 'cac']; + +describe.each(cliTools)('cli completion tests for %s', (cliTool) => { + const commandPrefix = `pnpm tsx demo.${cliTool}.ts complete --`; + + it('should complete cli options', async () => { + const output = await runCommand(`${commandPrefix}`); + expect(output).toMatchSnapshot(); + }); + + describe('cli option completion tests', () => { + const optionTests = [{ partial: '--p', expected: '--port' }]; + + test.each(optionTests)( + "should complete option for partial input '%s'", + async ({ partial }) => { + const command = `${commandPrefix} dev ${partial}`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + } + ); + }); + + describe('cli option exclusion tests', () => { + const alreadySpecifiedTests = [ + { specified: '--config', shouldNotContain: '--config' }, + ]; + + test.each(alreadySpecifiedTests)( + "should not suggest already specified option '%s'", + async ({ specified }) => { + const command = `${commandPrefix} ${specified} --`; + const output = await runCommand(command); + console.log(output) + expect(output).toMatchSnapshot(); + } + ); + }); + + describe('cli option value handling', () => { + it('should resolve port value correctly', async () => { + const command = `${commandPrefix} dev --port=3`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it('should not show duplicate options', async () => { + const command = `${commandPrefix} --config vite.config.js --`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it('should resolve config option values correctly', async () => { + const command = `${commandPrefix} --config vite.config`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it('should handle unknown options with no completions', async () => { + const command = `${commandPrefix} --unknownoption`; + const output = await runCommand(command); + expect(output.trim()).toMatchSnapshot(); + }); + }); + + describe('edge case completions for end with space', () => { + //TOOD: remove this + it('should suggest port values if user ends with space after `--port`', async () => { + const command = `${commandPrefix} dev --port ""`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it("should keep suggesting the --port option if user typed partial but didn't end with space", async () => { + const command = `${commandPrefix} dev --po`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it("should suggest port values if user typed `--port=` and hasn't typed a space or value yet", async () => { + const command = `${commandPrefix} dev --port=`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + }); + + // single positional command: `lint [file]` + // vite "" + // -> src/ + // -> ./ + + // vite src/ "" + // -> nothing + // should not suggest anything + + // multiple postiionals command `lint [...files]` + // vite "" + // -> src/ + // -> ./ + + // vite src/ "" + // -> src/ + // -> ./ + + describe('positional argument completions', () => { + it.runIf(cliTool !== 'citty')('should complete multiple positional arguments when ending with space', async () => { + const command = `${commandPrefix} lint ""`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it.runIf(cliTool !== 'citty')('should complete multiple positional arguments when ending with part of the value', async () => { + const command = `${commandPrefix} lint ind`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + + it.runIf(cliTool !== 'citty')('should complete single positional argument when ending with space', async () => { + const command = `${commandPrefix} lint main.ts ""`; + const output = await runCommand(command); + expect(output).toMatchSnapshot(); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6b328fd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,111 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "es2022" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node10" /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true /* Enable error reporting when local variables aren't read. */, + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}